From 5403f346e32d6af5106d031de4c0a2d0e48242f9 Mon Sep 17 00:00:00 2001 From: "TOMAHOST\\xes" Date: Thu, 14 Aug 2025 22:39:38 +0200 Subject: [PATCH] upgrade --- plugin/add_cas_login_button/README.md | 11 + plugin/add_cas_login_button/css.css | 12 + plugin/add_cas_login_button/index.php | 29 + plugin/add_cas_login_button/plugin.php | 49 + plugin/add_cas_login_button/template.tpl | 22 + plugin/add_cas_logout_button/README.md | 12 + plugin/add_cas_logout_button/css.css | 12 + plugin/add_cas_logout_button/index.php | 26 + plugin/add_cas_logout_button/plugin.php | 50 + plugin/add_cas_logout_button/template.tpl | 15 + plugin/add_facebook_login_button/README.md | 16 + plugin/add_facebook_login_button/css.css | 12 + plugin/add_facebook_login_button/index.php | 17 + plugin/add_facebook_login_button/plugin.php | 45 + plugin/add_facebook_login_button/template.tpl | 14 + plugin/add_shibboleth_login_button/README.md | 10 + plugin/add_shibboleth_login_button/css.css | 12 + plugin/add_shibboleth_login_button/index.php | 22 + plugin/add_shibboleth_login_button/plugin.php | 72 + .../add_shibboleth_login_button/template.tpl | 15 + plugin/advanced_subscription/README.md | 59 + .../ajax/advanced_subscription.ajax.php | 350 + plugin/advanced_subscription/config.php | 35 + .../cron/notify_by_mail.php | 153 + plugin/advanced_subscription/index.html | 1 + plugin/advanced_subscription/install.php | 17 + plugin/advanced_subscription/lang/english.php | 149 + plugin/advanced_subscription/lang/french.php | 148 + plugin/advanced_subscription/lang/spanish.php | 149 + plugin/advanced_subscription/license.txt | 1 + plugin/advanced_subscription/plugin.php | 13 + .../src/AdvancedSubscriptionPlugin.php | 1534 ++ .../src/HookAdvancedSubscription.php | 681 + .../advanced_subscription/src/admin_view.php | 104 + .../src/open_session.php | 61 + .../advanced_subscription/src/render_mail.php | 26 + .../src/scripts/insert_session_fields.php | 204 + .../scripts/insert_sessions_categories.php | 21 + .../src/terms_and_conditions.php | 85 + plugin/advanced_subscription/test/mails.php | 134 + .../test/terms_to_pdf.php | 47 + .../test/ws_session_user.php | 86 + plugin/advanced_subscription/uninstall.php | 18 + .../views/admin_accepted_notice_admin.tpl | 75 + .../views/admin_accepted_notice_student.tpl | 75 + .../views/admin_accepted_notice_superior.tpl | 75 + .../views/admin_rejected_notice_admin.tpl | 75 + .../views/admin_rejected_notice_student.tpl | 75 + .../views/admin_rejected_notice_superior.tpl | 75 + .../views/admin_view.tpl | 167 + .../advanced_subscription/views/css/style.css | 84 + .../views/img/aprobar.png | Bin 0 -> 1482 bytes .../views/img/avatar.png | Bin 0 -> 1117 bytes .../views/img/desaprobar.png | Bin 0 -> 1901 bytes .../views/img/footer.png | Bin 0 -> 270 bytes .../views/img/header.png | Bin 0 -> 267 bytes .../views/img/icon-avatar.png | Bin 0 -> 493 bytes .../advanced_subscription/views/img/line.png | Bin 0 -> 283 bytes .../views/open_session.tpl | 54 + .../views/reminder_notice_admin.tpl | 75 + .../views/reminder_notice_student.tpl | 76 + .../views/reminder_notice_superior.tpl | 86 + .../views/reminder_notice_superior_max.tpl | 87 + .../student_no_superior_notice_admin.tpl | 75 + .../student_no_superior_notice_student.tpl | 76 + .../views/student_notice_student.tpl | 75 + .../views/student_notice_superior.tpl | 84 + .../views/superior_accepted_notice_admin.tpl | 75 + .../superior_accepted_notice_student.tpl | 76 + .../superior_accepted_notice_superior.tpl | 76 + .../superior_rejected_notice_student.tpl | 75 + .../superior_rejected_notice_superior.tpl | 75 + .../views/terms_and_conditions.tpl | 61 + .../views/terms_and_conditions_to_pdf.tpl | 23 + plugin/ai_helper/AiHelperPlugin.php | 421 + plugin/ai_helper/Entity/Requests.php | 171 + plugin/ai_helper/README.md | 68 + plugin/ai_helper/install.php | 16 + plugin/ai_helper/lang/english.php | 20 + plugin/ai_helper/lang/french.php | 18 + plugin/ai_helper/lang/spanish.php | 21 + plugin/ai_helper/plugin.php | 6 + plugin/ai_helper/src/deepseek/DeepSeek.php | 91 + plugin/ai_helper/src/deepseek/DeepSeekUrl.php | 17 + plugin/ai_helper/src/openai/OpenAi.php | 341 + plugin/ai_helper/src/openai/OpenAiUrl.php | 74 + plugin/ai_helper/tool/answers.php | 30 + plugin/ai_helper/tool/learnpath.php | 218 + plugin/ai_helper/uninstall.php | 16 + plugin/azure_active_directory/CHANGELOG.md | 48 + plugin/azure_active_directory/README.md | 46 + plugin/azure_active_directory/index.php | 38 + plugin/azure_active_directory/install.php | 8 + plugin/azure_active_directory/lang/dutch.php | 54 + .../azure_active_directory/lang/english.php | 54 + plugin/azure_active_directory/lang/french.php | 54 + .../azure_active_directory/lang/spanish.php | 54 + .../layout/login_form.tpl | 51 + plugin/azure_active_directory/login.php | 35 + plugin/azure_active_directory/plugin.php | 10 + .../src/AzureActiveDirectory.php | 442 + .../src/AzureCommand.php | 239 + .../src/AzureSyncUsergroupsCommand.php | 74 + .../src/AzureSyncUsersCommand.php | 100 + .../src/Entity/AzureSyncState.php | 68 + .../azure_active_directory/src/callback.php | 166 + .../src/scripts/sync_usergroups.php | 21 + .../src/scripts/sync_users.php | 21 + plugin/azure_active_directory/uninstall.php | 9 + plugin/azure_active_directory/view/block.tpl | 22 + plugin/azure_active_directory/view/login.tpl | 26 + plugin/bbb/CHANGELOG.md | 61 + plugin/bbb/README.md | 95 + plugin/bbb/admin.php | 166 + plugin/bbb/ajax.php | 47 + plugin/bbb/config.php | 6 + plugin/bbb/config.vm.dist.php | 38 + plugin/bbb/course_index.php | 11 + plugin/bbb/cron.php | 28 + plugin/bbb/cron_close_meeting.php | 113 + plugin/bbb/index.php | 1 + plugin/bbb/install.php | 10 + plugin/bbb/lang/english.php | 78 + plugin/bbb/lang/french.php | 71 + plugin/bbb/lang/german.php | 65 + plugin/bbb/lang/spanish.php | 71 + plugin/bbb/lib/VM.php | 116 + plugin/bbb/lib/bbb.lib.php | 2232 +++ plugin/bbb/lib/bbb_api.php | 697 + plugin/bbb/lib/bbb_plugin.class.php | 407 + plugin/bbb/lib/vm/AbstractVM.php | 50 + plugin/bbb/lib/vm/AmazonVM.php | 44 + plugin/bbb/lib/vm/DigitalOceanVM.php | 180 + plugin/bbb/lib/vm/VMInterface.php | 29 + plugin/bbb/listing.php | 540 + plugin/bbb/plugin.php | 6 + .../bbb/resources/img/128/bigbluebutton.png | Bin 0 -> 3380 bytes .../resources/img/128/bigbluebutton_na.png | Bin 0 -> 3671 bytes .../img/128/videoconference_flash.png | Bin 0 -> 6506 bytes .../img/128/videoconference_html5.png | Bin 0 -> 7475 bytes plugin/bbb/resources/img/22/bigbluebutton.png | Bin 0 -> 740 bytes .../bbb/resources/img/22/bigbluebutton_na.png | Bin 0 -> 774 bytes plugin/bbb/resources/img/32/bigbluebutton.png | Bin 0 -> 977 bytes .../bbb/resources/img/32/bigbluebutton_na.png | Bin 0 -> 1041 bytes plugin/bbb/resources/img/64/bigbluebutton.png | Bin 0 -> 1750 bytes .../bbb/resources/img/64/bigbluebutton_na.png | Bin 0 -> 1892 bytes .../img/64/videoconference_flash.png | Bin 0 -> 3073 bytes .../img/64/videoconference_html5.png | Bin 0 -> 3371 bytes .../bbb/resources/img/svg/bigbluebutton.svg | 284 + .../resources/img/svg/bigbluebutton_na.svg | 336 + .../img/svg/videoconference_flash.svg | 282 + .../img/svg/videoconference_html5.svg | 327 + plugin/bbb/resources/utils.js | 27 + plugin/bbb/start.php | 112 + plugin/bbb/uninstall.php | 12 + plugin/bbb/update.php | 14 + plugin/bbb/view/admin.tpl | 58 + plugin/bbb/view/listing.tpl | 146 + plugin/before_login/README.md | 14 + plugin/before_login/index.php | 76 + plugin/before_login/plugin.php | 53 + plugin/before_login/template.tpl | 14 + plugin/buycourses/CHANGELOG.md | 134 + plugin/buycourses/README.md | 37 + plugin/buycourses/admin.php | 3 + plugin/buycourses/config.php | 4 + plugin/buycourses/database.php | 996 + plugin/buycourses/index.php | 7 + plugin/buycourses/install.php | 16 + plugin/buycourses/lang/brazilian.php | 99 + plugin/buycourses/lang/dutch.php | 135 + plugin/buycourses/lang/english.php | 321 + plugin/buycourses/lang/french.php | 250 + plugin/buycourses/lang/spanish.php | 321 + plugin/buycourses/plugin.php | 13 + plugin/buycourses/resources/css/style.css | 421 + .../buycourses/resources/img/128/backlogs.png | Bin 0 -> 7904 bytes .../resources/img/128/buycourses.png | Bin 0 -> 10381 bytes .../resources/img/128/buysubscriptions.png | Bin 0 -> 3816 bytes .../buycourses/resources/img/128/discount.png | Bin 0 -> 15680 bytes .../resources/img/128/paymentsettings.png | Bin 0 -> 13852 bytes .../buycourses/resources/img/128/settings.png | Bin 0 -> 11522 bytes .../img/128/subscriptionssettings.png | Bin 0 -> 6629 bytes .../buycourses/resources/img/32/backlogs.png | Bin 0 -> 1338 bytes .../resources/img/32/buycourses.png | Bin 0 -> 1691 bytes .../resources/img/32/buysubscription.png | Bin 0 -> 1263 bytes .../resources/img/32/buysubscriptions.png | Bin 0 -> 1263 bytes .../buycourses/resources/img/32/discount.png | Bin 0 -> 4181 bytes .../resources/img/32/paymentsettings.png | Bin 0 -> 2074 bytes .../buycourses/resources/img/32/settings.png | Bin 0 -> 1695 bytes .../resources/img/32/subscriptionsettings.png | Bin 0 -> 1680 bytes .../img/32/subscriptionssettings.png | Bin 0 -> 1680 bytes .../buycourses/resources/img/42/backlogs.png | Bin 0 -> 2200 bytes .../resources/img/42/buycourses.png | Bin 0 -> 2794 bytes .../resources/img/42/buysubscriptions.png | Bin 0 -> 1263 bytes .../buycourses/resources/img/42/discount.png | Bin 0 -> 5070 bytes .../resources/img/42/paymentsettings.png | Bin 0 -> 3729 bytes .../buycourses/resources/img/42/settings.png | Bin 0 -> 3070 bytes .../img/42/subscriptionssettings.png | Bin 0 -> 1680 bytes .../buycourses/resources/img/64/backlogs.png | Bin 0 -> 3253 bytes .../resources/img/64/buycourses.png | Bin 0 -> 4067 bytes .../resources/img/64/buysubscriptions.png | Bin 0 -> 2148 bytes .../buycourses/resources/img/64/discount.png | Bin 0 -> 6449 bytes .../resources/img/64/paymentsettings.png | Bin 0 -> 5561 bytes .../buycourses/resources/img/64/settings.png | Bin 0 -> 4475 bytes .../img/64/subscriptionssettings.png | Bin 0 -> 3455 bytes plugin/buycourses/resources/js/commissions.js | 145 + plugin/buycourses/resources/js/modals.js | 1 + .../src/buy_course_plugin.class.php | 6146 +++++++ plugin/buycourses/src/buycourses.ajax.php | 612 + plugin/buycourses/src/cecabank_cancel.php | 30 + plugin/buycourses/src/cecabank_response.php | 53 + plugin/buycourses/src/cecabank_success.php | 30 + plugin/buycourses/src/configure_coupon.php | 204 + plugin/buycourses/src/configure_course.php | 363 + plugin/buycourses/src/configure_frequency.php | 119 + .../buycourses/src/configure_subscription.php | 246 + plugin/buycourses/src/coupon_add.php | 167 + plugin/buycourses/src/coupons.php | 109 + plugin/buycourses/src/course_catalog.php | 100 + plugin/buycourses/src/course_panel.php | 67 + plugin/buycourses/src/error.php | 14 + plugin/buycourses/src/export_report.php | 63 + .../src/export_subscription_report.php | 63 + plugin/buycourses/src/expresscheckout.php | 47 + plugin/buycourses/src/index.buycourses.php | 48 + plugin/buycourses/src/index.php | 1 + plugin/buycourses/src/invoice.php | 155 + plugin/buycourses/src/list.php | 87 + plugin/buycourses/src/list_coupon.php | 96 + plugin/buycourses/src/list_service.php | 71 + plugin/buycourses/src/list_session.php | 89 + plugin/buycourses/src/panel.ajax.php | 221 + plugin/buycourses/src/paymentsetup.php | 538 + plugin/buycourses/src/payout_panel.php | 65 + plugin/buycourses/src/payout_report.php | 94 + plugin/buycourses/src/paypal_payout.php | 55 + plugin/buycourses/src/paypalfunctions.php | 432 + plugin/buycourses/src/process.php | 188 + plugin/buycourses/src/process_confirm.php | 597 + .../src/process_subscription_confirm.php | 375 + plugin/buycourses/src/sales_report.php | 216 + plugin/buycourses/src/service_catalog.php | 101 + plugin/buycourses/src/service_error.php | 35 + plugin/buycourses/src/service_information.php | 42 + plugin/buycourses/src/service_panel.php | 74 + plugin/buycourses/src/service_process.php | 304 + .../src/service_process_confirm.php | 385 + .../buycourses/src/service_sales_report.php | 103 + plugin/buycourses/src/service_success.php | 186 + plugin/buycourses/src/services_add.php | 141 + plugin/buycourses/src/services_edit.php | 161 + plugin/buycourses/src/session_catalog.php | 127 + plugin/buycourses/src/session_panel.php | 67 + plugin/buycourses/src/stripe_cancel.php | 30 + plugin/buycourses/src/stripe_response.php | 65 + plugin/buycourses/src/stripe_success.php | 78 + plugin/buycourses/src/subscription_add.php | 234 + .../src/subscription_course_catalog.php | 88 + .../buycourses/src/subscription_process.php | 266 + .../src/subscription_sales_report.php | 201 + .../src/subscription_session_catalog.php | 114 + .../buycourses/src/subscriptions_courses.php | 97 + .../buycourses/src/subscriptions_sessions.php | 101 + plugin/buycourses/src/success.php | 200 + plugin/buycourses/src/tpv_error.php | 30 + plugin/buycourses/src/tpv_response.php | 68 + plugin/buycourses/src/tpv_success.php | 31 + plugin/buycourses/uninstall.php | 15 + plugin/buycourses/update.php | 14 + .../uploads/services/images/.gitignore | 3 + plugin/buycourses/view/catalog.tpl | 223 + .../buycourses/view/configure_frequency.tpl | 32 + .../view/configure_subscription.tpl | 45 + plugin/buycourses/view/coupons.tpl | 39 + plugin/buycourses/view/course_panel.tpl | 48 + plugin/buycourses/view/export_report.tpl | 1 + plugin/buycourses/view/index.tpl | 117 + plugin/buycourses/view/list.tpl | 231 + plugin/buycourses/view/list_coupon.tpl | 49 + plugin/buycourses/view/list_subscription.tpl | 0 plugin/buycourses/view/message_confirm.tpl | 14 + .../buycourses/view/message_confirm_buyer.tpl | 16 + plugin/buycourses/view/message_transfer.tpl | 37 + plugin/buycourses/view/paymentsetup.tpl | 171 + plugin/buycourses/view/payout_panel.tpl | 50 + plugin/buycourses/view/payout_report.tpl | 99 + plugin/buycourses/view/paypal_payout.tpl | 131 + plugin/buycourses/view/process.tpl | 167 + plugin/buycourses/view/process_confirm.tpl | 283 + plugin/buycourses/view/sales_report.tpl | 55 + .../buycourses/view/service_information.tpl | 108 + .../view/service_message_transfer.tpl | 36 + plugin/buycourses/view/service_panel.tpl | 85 + plugin/buycourses/view/service_process.tpl | 120 + .../buycourses/view/service_sales_report.tpl | 121 + plugin/buycourses/view/session_panel.tpl | 48 + plugin/buycourses/view/subscription_add.tpl | 37 + .../buycourses/view/subscription_catalog.tpl | 122 + .../buycourses/view/subscription_process.tpl | 187 + .../view/subscription_process_confirm.tpl | 283 + .../view/subscription_sales_report.tpl | 122 + plugin/buycourses/view/subscriptions.tpl | 152 + plugin/buycourses/view/success.tpl | 70 + plugin/card_game/LICENSE.txt | 674 + plugin/card_game/README.md | 26 + plugin/card_game/card_game.php | 51 + plugin/card_game/index.php | 106 + plugin/card_game/install.php | 9 + plugin/card_game/lang/english.php | 5 + plugin/card_game/lang/french.php | 5 + plugin/card_game/lang/spanish.php | 5 + plugin/card_game/plugin.php | 21 + plugin/card_game/resources/ajax.card.php | 50 + plugin/card_game/resources/css/animstar.png | Bin 0 -> 5899 bytes plugin/card_game/resources/css/b-back.jpg | Bin 0 -> 9652 bytes plugin/card_game/resources/css/b-back.png | Bin 0 -> 266096 bytes .../resources/css/card-acces-active.svg | 112 + plugin/card_game/resources/css/card-acces.svg | 92 + plugin/card_game/resources/css/card-back.png | Bin 0 -> 38672 bytes plugin/card_game/resources/css/card1.jpg | Bin 0 -> 15890 bytes plugin/card_game/resources/css/card1.pdn | Bin 0 -> 54964 bytes plugin/card_game/resources/css/cardgame.css | 238 + plugin/card_game/resources/css/cardpack.jpg | Bin 0 -> 58279 bytes .../card_game/resources/css/cardpack450.jpg | Bin 0 -> 40703 bytes plugin/card_game/resources/css/galerie.png | Bin 0 -> 4081 bytes plugin/card_game/resources/css/img01.jpg | Bin 0 -> 205768 bytes plugin/card_game/resources/css/img02.jpg | Bin 0 -> 125390 bytes plugin/card_game/resources/css/img03.jpg | Bin 0 -> 300153 bytes plugin/card_game/resources/css/img04.jpg | Bin 0 -> 233104 bytes .../card_game/resources/css/renderchami.jpg | Bin 0 -> 28026 bytes .../card_game/resources/css/scissorsright.gif | Bin 0 -> 5974 bytes plugin/card_game/resources/js/cardgame.js | 237 + plugin/card_game/uninstall.php | 9 + .../CheckExtraFieldAuthorsCompanyPlugin.php | 553 + .../README.md | 30 + .../index.php | 5 + .../install.php | 9 + .../plugin.php | 17 + .../uninstall.php | 8 + plugin/cleandeletedfiles/README.md | 6 + plugin/cleandeletedfiles/admin.php | 192 + plugin/cleandeletedfiles/config.php | 10 + plugin/cleandeletedfiles/index.php | 5 + plugin/cleandeletedfiles/install.php | 1 + plugin/cleandeletedfiles/lang/english.php | 17 + plugin/cleandeletedfiles/lang/french.php | 17 + plugin/cleandeletedfiles/lang/spanish.php | 17 + plugin/cleandeletedfiles/plugin.php | 11 + .../src/CleanDeletedFilesPlugin.php | 34 + plugin/cleandeletedfiles/src/ajax.php | 47 + plugin/clockworksms/README.md | 6 + plugin/clockworksms/config.php | 15 + plugin/clockworksms/index.php | 1 + plugin/clockworksms/install.php | 13 + plugin/clockworksms/lang/english.php | 102 + plugin/clockworksms/lang/spanish.php | 102 + plugin/clockworksms/lib/clockworksms.lib.php | 676 + .../lib/clockworksms_plugin.class.php | 50 + plugin/clockworksms/license.txt | 5 + plugin/clockworksms/plugin.php | 11 + .../account_approved_connect.tpl | 1 + ...account_created_updated_login_password.tpl | 1 + .../answer_open_question_test_reviewed.tpl | 1 + .../assignment_been_created_course.tpl | 1 + .../been_invited_complete_survey_course.tpl | 1 + .../sms_templates/been_invited_join_group.tpl | 1 + .../sms_templates/been_subscribed_course.tpl | 1 + .../certificate_notification.tpl | 1 + .../check_email_connect_more_info.tpl | 1 + ...course_opening_request_code_registered.tpl | 1 + .../course_opening_request_course_code.tpl | 1 + ...e_opening_request_course_code_approved.tpl | 1 + .../messages_sent_edited_group_added.tpl | 1 + .../messages_sent_edited_group_edited.tpl | 1 + .../sms_templates/new_answer_posted_forum.tpl | 1 + .../sms_templates/new_course_been_created.tpl | 1 + .../new_course_suggested_teacher.tpl | 1 + .../new_file_shared_course_by.tpl | 1 + .../new_system_announcement_added.tpl | 1 + .../new_thread_started_forum.tpl | 1 + .../new_user_pending_approval.tpl | 1 + .../new_user_subscribed_course.tpl | 1 + .../password_updated_login_password.tpl | 1 + .../sms_templates/posted_forum_course.tpl | 1 + .../received_new_personal_messages.tpl | 1 + .../reminder_assignment_course_due.tpl | 1 + .../request_open_course_code_rejected.tpl | 1 + .../requested_password_change.tpl | 1 + .../sms_templates/student_answered_test.tpl | 1 + .../student_answered_test_open_question.tpl | 1 + .../student_answered_test_voice_question.tpl | 1 + .../sms_templates/subscribed_session.tpl | 1 + .../sms_templates/subscribed_session_csv.tpl | 1 + .../system_announcement_update.tpl | 1 + .../test_new_system_announcement_added.tpl | 1 + .../test_system_announcement_update.tpl | 1 + .../user_answered_inbox_message.tpl | 1 + .../sms_templates/user_details_modified.tpl | 1 + .../user_suggested_be_friends.tpl | 1 + .../user_uploaded_assignment_check.tpl | 1 + ...assignment_check_student_submits_paper.tpl | 1 + .../user_uploaded_assignment_course.tpl | 1 + ...ssignment_course_student_submits_paper.tpl | 1 + .../sms_templates/welcome_login_password.tpl | 1 + plugin/clockworksms/uninstall.php | 14 + plugin/clockworksms/vendor/changelog.md | 22 + .../clockworksms/vendor/clockworksms_api.php | 643 + plugin/clockworksms/vendor/exception.php | 27 + plugin/clockworksms/vendor/license.txt | 14 + plugin/clockworksms/vendor/license.txt~ | 13 + plugin/clockworksms/vendor/readme.md | 253 + plugin/courseblock/CourseBlockPlugin.php | 94 + plugin/courseblock/README.md | 8 + plugin/courseblock/config.php | 4 + plugin/courseblock/index.php | 1 + plugin/courseblock/install.php | 9 + plugin/courseblock/lang/english.php | 11 + plugin/courseblock/lang/french.php | 1 + plugin/courseblock/lang/spanish.php | 1 + plugin/courseblock/plugin.php | 4 + .../CourseHomeNotifyPlugin.php | 202 + .../coursehomenotify/Entity/Notification.php | 152 + .../Entity/NotificationRelUser.php | 99 + plugin/coursehomenotify/README.md | 17 + plugin/coursehomenotify/configure.php | 100 + plugin/coursehomenotify/content.php | 52 + plugin/coursehomenotify/install.php | 8 + plugin/coursehomenotify/lang/english.php | 17 + plugin/coursehomenotify/lang/spanish.php | 17 + plugin/coursehomenotify/plugin.php | 4 + plugin/coursehomenotify/uninstall.php | 8 + plugin/courselegal/CourseLegalPlugin.php | 552 + plugin/courselegal/README.md | 25 + plugin/courselegal/config.php | 4 + plugin/courselegal/index.php | 1 + plugin/courselegal/install.php | 9 + plugin/courselegal/lang/english.php | 23 + plugin/courselegal/lang/french.php | 23 + plugin/courselegal/lang/spanish.php | 23 + plugin/courselegal/plugin.php | 4 + plugin/courselegal/start.php | 64 + plugin/courselegal/user_list.php | 81 + plugin/createdrupaluser/README.md | 28 + plugin/createdrupaluser/config.php | 10 + plugin/createdrupaluser/index.php | 10 + plugin/createdrupaluser/install.php | 12 + plugin/createdrupaluser/lang/english.php | 16 + plugin/createdrupaluser/lang/spanish.php | 16 + plugin/createdrupaluser/plugin.php | 12 + .../createdrupaluser/src/CreateDrupalUser.php | 156 + .../src/HookCreateDrupalUser.php | 78 + plugin/createdrupaluser/uninstall.php | 12 + plugin/customcertificate/README.md | 26 + plugin/customcertificate/config.php | 11 + plugin/customcertificate/database.php | 68 + plugin/customcertificate/index.php | 3 + plugin/customcertificate/install.php | 15 + plugin/customcertificate/lang/english.php | 77 + plugin/customcertificate/lang/spanish.php | 79 + plugin/customcertificate/plugin.php | 16 + .../resources/css/certificate.css | 11 + .../customcertificate/resources/css/form.css | 18 + .../resources/img/22/customcertificate.png | Bin 0 -> 843 bytes .../resources/img/64/customcertificate.png | Bin 0 -> 2871 bytes .../resources/img/64/customcertificate_na.png | Bin 0 -> 3034 bytes .../resources/js/certificate.js | 38 + .../src/CustomCertificatePlugin.php | 332 + .../src/customcertificate.ajax.php | 51 + .../src/export_pdf_all_in_one.php | 708 + plugin/customcertificate/src/index.php | 978 + .../src/print_certificate.php | 598 + plugin/customcertificate/start.php | 34 + plugin/customcertificate/uninstall.php | 12 + plugin/customcertificate/update.php | 14 + plugin/customfooter/index.php | 14 + plugin/customfooter/install.php | 29 + plugin/customfooter/lang/en/tool_delivery.php | 71 + plugin/customfooter/lang/fr/tool_delivery.php | 66 + .../lib/customfooter_plugin.class.php | 54 + plugin/customfooter/plugin.php | 56 + .../block_course/block_course.class.php | 209 + .../dashboard/block_course/block_course.info | 6 + plugin/dashboard/block_course/index.html | 8 + .../block_daily/block_daily.class.php | 269 + plugin/dashboard/block_daily/block_daily.info | 6 + plugin/dashboard/block_daily/index.html | 8 + .../block_evaluation_graph.class.php | 489 + .../block_evaluation_graph.info | 6 + .../block_global_info.class.php | 152 + .../block_global_info/block_global_info.info | 6 + plugin/dashboard/block_global_info/index.html | 8 + .../block_session/block_session.class.php | 164 + .../block_session/block_session.info | 6 + plugin/dashboard/block_session/index.html | 8 + .../block_student/block_student.class.php | 259 + .../block_student/block_student.info | 6 + plugin/dashboard/block_student/index.html | 8 + .../block_student_graph.class.php | 263 + .../block_student_graph.info | 6 + .../block_teacher/block_teacher.class.php | 212 + .../block_teacher/block_teacher.info | 6 + plugin/dashboard/block_teacher/index.html | 8 + .../block_teacher_graph.class.php | 221 + .../block_teacher_graph.info | 6 + plugin/dashboard/css/default.css | 80 + plugin/dashboard/index.html | 8 + plugin/date/README.md | 8 + plugin/date/index.act.php | 22 + plugin/date/index.dsp.php | 10 + plugin/date/index.php | 11 + plugin/date/plugin.php | 20 + plugin/date/start.php | 10 + plugin/dictionary/DictionaryPlugin.php | 55 + plugin/dictionary/index.php | 2 + plugin/dictionary/install.php | 7 + plugin/dictionary/lang/english.php | 6 + plugin/dictionary/plugin.php | 4 + plugin/dictionary/terms.php | 82 + plugin/dictionary/view/terms.html.twig | 34 + plugin/embedregistry/EmbedRegistryPlugin.php | 250 + plugin/embedregistry/Entity/Embed.php | 198 + plugin/embedregistry/README.md | 14 + plugin/embedregistry/install.php | 4 + plugin/embedregistry/lang/english.php | 22 + plugin/embedregistry/lang/french.php | 22 + plugin/embedregistry/lang/spanish.php | 22 + plugin/embedregistry/plugin.php | 4 + plugin/embedregistry/start.php | 309 + plugin/embedregistry/uninstall.php | 4 + plugin/embedregistry/view.php | 72 + plugin/embedregistry/view/start.tpl | 37 + plugin/exercise_signature/README.md | 15 + plugin/exercise_signature/index.php | 1 + plugin/exercise_signature/install.php | 9 + plugin/exercise_signature/lang/english.php | 12 + plugin/exercise_signature/lang/french.php | 12 + plugin/exercise_signature/lang/spanish.php | 11 + .../lib/ExerciseSignature.php | 206 + plugin/exercise_signature/plugin.php | 5 + plugin/exercise_signature/uninstall.php | 9 + plugin/exercisefocused/admin.php | 32 + plugin/exercisefocused/index.php | 48 + plugin/exercisefocused/install.php | 5 + plugin/exercisefocused/lang/english.php | 39 + plugin/exercisefocused/lang/spanish.php | 39 + plugin/exercisefocused/pages/detail.php | 32 + plugin/exercisefocused/pages/export.php | 298 + plugin/exercisefocused/pages/log.php | 30 + plugin/exercisefocused/pages/reporting.php | 34 + plugin/exercisefocused/plugin.php | 10 + .../src/Controller/AdminController.php | 52 + .../src/Controller/BaseController.php | 86 + .../src/Controller/DetailController.php | 98 + .../src/Controller/LogController.php | 97 + .../src/Controller/ReportingController.php | 138 + plugin/exercisefocused/src/Entity/Log.php | 99 + .../src/ExerciseFocusedPlugin.php | 213 + .../src/Repository/LogRepository.php | 28 + .../src/Traits/DetailControllerTrait.php | 28 + .../src/Traits/ReportingFilterTrait.php | 393 + .../exercisefocused/templates/block.html.twig | 105 + .../templates/script.html.twig | 163 + plugin/exercisefocused/uninstall.php | 5 + plugin/exercisemonitoring/admin.php | 3 + .../assets/images/idcard.png | Bin 0 -> 7398 bytes .../exercisemonitoring/assets/images/user.png | Bin 0 -> 9957 bytes plugin/exercisemonitoring/cron/cleanup.php | 97 + plugin/exercisemonitoring/index.php | 61 + plugin/exercisemonitoring/install.php | 5 + plugin/exercisemonitoring/lang/english.php | 32 + plugin/exercisemonitoring/lang/spanish.php | 32 + plugin/exercisemonitoring/pages/detail.php | 32 + .../pages/exercise_submit.ajax.php | 18 + .../exercisemonitoring/pages/start.ajax.php | 18 + plugin/exercisemonitoring/plugin.php | 10 + .../src/Controller/DetailController.php | 111 + .../Controller/ExerciseSubmitController.php | 119 + .../src/Controller/StartController.php | 93 + plugin/exercisemonitoring/src/Entity/Log.php | 133 + .../src/ExerciseMonitoringPlugin.php | 185 + .../src/Repository/LogRepository.php | 47 + .../templates/exercise_submit.html.twig | 84 + .../templates/modal.html.twig | 314 + plugin/exercisemonitoring/uninstall.php | 5 + .../README.md | 20 + .../index.php | 42 + .../plugin.php | 61 + .../template.tpl | 12 + plugin/externalnotificationconnect/README.md | 22 + .../externalnotificationconnect/install.php | 5 + .../lang/english.php | 18 + .../lang/french.php | 18 + .../lang/spanish.php | 18 + plugin/externalnotificationconnect/plugin.php | 5 + .../src/Entity/AccessToken.php | 71 + .../src/ExternalNotificationConnectPlugin.php | 241 + ...xternalNotificationConnectHookObserver.php | 21 + ...ConnectLearningPathCreatedHookObserver.php | 56 + ...nConnectPortfolioItemAddedHookObserver.php | 50 + ...onnectPortfolioItemDeletedHookObserver.php | 33 + ...ConnectPortfolioItemEditedHookObserver.php | 50 + ...ectPortfolioItemVisibilityHookObserver.php | 44 + .../src/Traits/RequestTrait.php | 191 + .../externalnotificationconnect/uninstall.php | 5 + plugin/extramenufromwebservice/LICENSE.txt | 674 + plugin/extramenufromwebservice/README.md | 10 + plugin/extramenufromwebservice/config.php | 4 + plugin/extramenufromwebservice/index.php | 114 + plugin/extramenufromwebservice/install.php | 7 + .../extramenufromwebservice/lang/english.php | 15 + .../extramenufromwebservice/lang/french.php | 15 + .../extramenufromwebservice/lang/spanish.php | 15 + .../extramenufromwebservice_plugin.class.php | 189 + plugin/extramenufromwebservice/plugin.php | 5 + .../resources/css/extramenufromwebservice.css | 108 + .../resources/js/extramenufromwebservice.js | 8 + plugin/extramenufromwebservice/uninstall.php | 5 + plugin/follow_buttons/README.md | 6 + plugin/follow_buttons/index.php | 20 + plugin/follow_buttons/plugin.php | 31 + plugin/follow_buttons/template.tpl | 35 + plugin/formLogin_hide_unhide/README.md | 7 + plugin/formLogin_hide_unhide/css.css | 3 + plugin/formLogin_hide_unhide/index.php | 13 + plugin/formLogin_hide_unhide/plugin.php | 39 + plugin/formLogin_hide_unhide/template.tpl | 11 + plugin/google_maps/README.md | 4 + plugin/google_maps/config.php | 10 + plugin/google_maps/index.php | 12 + plugin/google_maps/install.php | 12 + plugin/google_maps/lang/english.php | 13 + plugin/google_maps/lang/french.php | 13 + plugin/google_maps/lang/spanish.php | 13 + plugin/google_maps/plugin.php | 12 + plugin/google_maps/src/GoogleMapsPlugin.php | 70 + plugin/google_maps/src/map_coordinates.php | 68 + plugin/google_maps/uninstall.php | 12 + plugin/google_maps/view/map_coordinates.tpl | 105 + plugin/google_meet/README.md | 18 + plugin/google_meet/config.php | 10 + plugin/google_meet/install.php | 10 + plugin/google_meet/lang/english.php | 22 + plugin/google_meet/lang/french.php | 22 + plugin/google_meet/lang/spanish.php | 22 + plugin/google_meet/meets.php | 226 + plugin/google_meet/plugin.php | 4 + plugin/google_meet/resources/css/style.css | 148 + .../resources/img/22/google_meet.png | Bin 0 -> 5030 bytes .../resources/img/22/google_meet_na.png | Bin 0 -> 3683 bytes .../resources/img/32/google_meet.png | Bin 0 -> 3038 bytes .../resources/img/32/google_meet_na.png | Bin 0 -> 2357 bytes .../resources/img/64/google_meet.png | Bin 0 -> 2629 bytes .../resources/img/64/google_meet_na.png | Bin 0 -> 2149 bytes .../resources/img/svg/googlemeet.svg | 304 + .../resources/img/svg/meet_room.svg | 1413 ++ .../src/google_meet_plugin.class.php | 396 + plugin/google_meet/start.php | 44 + plugin/google_meet/uninstall.php | 4 + plugin/google_meet/view/home.tpl | 70 + plugin/google_meet/view/meets.tpl | 15 + plugin/grading_electronic/README.md | 16 + plugin/grading_electronic/generate.php | 184 + plugin/grading_electronic/index.php | 16 + plugin/grading_electronic/install.php | 4 + plugin/grading_electronic/lang/english.php | 16 + plugin/grading_electronic/plugin.php | 5 + .../src/GradingElectronicPlugin.php | 221 + plugin/grading_electronic/uninstall.php | 4 + .../grading_electronic/view/grading.html.twig | 40 + plugin/h5p/README.md | 36 + plugin/h5p/cache-h5p/.eslintrc.json | 24 + .../cache-h5p/.github/workflows/cypress.yml | 16 + plugin/h5p/cache-h5p/.gitignore | 3 + plugin/h5p/cache-h5p/CHANGELOG.md | 17 + plugin/h5p/cache-h5p/LICENSE | 22 + plugin/h5p/cache-h5p/README.md | 85 + plugin/h5p/cache-h5p/cypress.json | 1 + .../cypress/integration/multiple.spec.js | 14 + .../cypress/integration/single.spec.js | 16 + plugin/h5p/cache-h5p/cypress/plugins/index.js | 17 + .../h5p/cache-h5p/cypress/support/commands.js | 25 + plugin/h5p/cache-h5p/cypress/support/index.js | 20 + .../h5p/cache-h5p/dist/fonts/h5p-core-23.eot | Bin 0 -> 9188 bytes .../h5p/cache-h5p/dist/fonts/h5p-core-23.svg | 62 + .../h5p/cache-h5p/dist/fonts/h5p-core-23.ttf | Bin 0 -> 9020 bytes .../h5p/cache-h5p/dist/fonts/h5p-core-23.woff | Bin 0 -> 9096 bytes plugin/h5p/cache-h5p/dist/frame.bundle.js | 3 + plugin/h5p/cache-h5p/dist/main.bundle.js | 3 + .../h5p/cache-h5p/dist/styles/h5p-admin.css | 358 + .../dist/styles/h5p-confirmation-dialog.css | 183 + .../cache-h5p/dist/styles/h5p-core-button.css | 60 + plugin/h5p/cache-h5p/dist/styles/h5p.css | 584 + plugin/h5p/cache-h5p/launch/dialog-card.html | 24 + .../h5p/cache-h5p/launch/drag-the-words.html | 23 + .../h5p/cache-h5p/launch/guesstheanswer.html | 23 + plugin/h5p/cache-h5p/launch/memory.html | 23 + plugin/h5p/cache-h5p/launch/mots-caches.html | 23 + plugin/h5p/cache-h5p/launch/multiple.html | 34 + .../css/drop-theme-arrows-bounce.min.css | 1 + .../source-dialogcard/Drop-1.0/js/drop.min.js | 7 + .../source-dialogcard/Drop-1.0/library.json | 28 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../H5P.Audio-1.4/language/af.json | 49 + .../H5P.Audio-1.4/language/ar.json | 49 + .../H5P.Audio-1.4/language/bg.json | 49 + .../H5P.Audio-1.4/language/bs.json | 49 + .../H5P.Audio-1.4/language/ca.json | 49 + .../H5P.Audio-1.4/language/cs.json | 49 + .../H5P.Audio-1.4/language/da.json | 49 + .../H5P.Audio-1.4/language/de.json | 49 + .../H5P.Audio-1.4/language/el.json | 49 + .../H5P.Audio-1.4/language/es-mx.json | 49 + .../H5P.Audio-1.4/language/es.json | 49 + .../H5P.Audio-1.4/language/et.json | 49 + .../H5P.Audio-1.4/language/eu.json | 49 + .../H5P.Audio-1.4/language/fi.json | 49 + .../H5P.Audio-1.4/language/fr.json | 49 + .../H5P.Audio-1.4/language/he.json | 49 + .../H5P.Audio-1.4/language/hu.json | 49 + .../H5P.Audio-1.4/language/it.json | 49 + .../H5P.Audio-1.4/language/ja.json | 49 + .../H5P.Audio-1.4/language/ko.json | 49 + .../H5P.Audio-1.4/language/nb.json | 49 + .../H5P.Audio-1.4/language/nl.json | 49 + .../H5P.Audio-1.4/language/nn.json | 49 + .../H5P.Audio-1.4/language/pl.json | 49 + .../H5P.Audio-1.4/language/pt-br.json | 49 + .../H5P.Audio-1.4/language/pt.json | 49 + .../H5P.Audio-1.4/language/ro.json | 49 + .../H5P.Audio-1.4/language/ru.json | 49 + .../H5P.Audio-1.4/language/sl.json | 49 + .../H5P.Audio-1.4/language/sma.json | 49 + .../H5P.Audio-1.4/language/sme.json | 49 + .../H5P.Audio-1.4/language/smj.json | 49 + .../H5P.Audio-1.4/language/sr.json | 49 + .../H5P.Audio-1.4/language/sv.json | 49 + .../H5P.Audio-1.4/language/tr.json | 49 + .../H5P.Audio-1.4/language/vi.json | 49 + .../H5P.Audio-1.4/language/zh-hans.json | 49 + .../H5P.Audio-1.4/language/zh-hant.json | 49 + .../H5P.Audio-1.4/language/zh.json | 49 + .../H5P.Audio-1.4/library.json | 40 + .../H5P.Audio-1.4/scripts/audio.js | 386 + .../H5P.Audio-1.4/semantics.json | 86 + .../H5P.Audio-1.4/styles/audio.css | 112 + .../H5P.Audio-1.4/upgrades.js | 54 + .../dist/h5p-dialogcards.css | 1 + .../dist/h5p-dialogcards.js | 1 + .../H5P.Dialogcards-1.8/icon.svg | 54 + .../H5P.Dialogcards-1.8/language/ar.json | 220 + .../H5P.Dialogcards-1.8/language/bg.json | 220 + .../H5P.Dialogcards-1.8/language/bs.json | 220 + .../H5P.Dialogcards-1.8/language/cs.json | 220 + .../H5P.Dialogcards-1.8/language/da.json | 220 + .../H5P.Dialogcards-1.8/language/de.json | 220 + .../H5P.Dialogcards-1.8/language/el.json | 220 + .../H5P.Dialogcards-1.8/language/es-mx.json | 220 + .../H5P.Dialogcards-1.8/language/es.json | 220 + .../H5P.Dialogcards-1.8/language/et.json | 220 + .../H5P.Dialogcards-1.8/language/eu.json | 220 + .../H5P.Dialogcards-1.8/language/fi.json | 220 + .../H5P.Dialogcards-1.8/language/fr.json | 220 + .../H5P.Dialogcards-1.8/language/he.json | 220 + .../H5P.Dialogcards-1.8/language/hr.json | 220 + .../H5P.Dialogcards-1.8/language/it.json | 220 + .../H5P.Dialogcards-1.8/language/ja.json | 220 + .../H5P.Dialogcards-1.8/language/nb.json | 220 + .../H5P.Dialogcards-1.8/language/nl.json | 220 + .../H5P.Dialogcards-1.8/language/nn.json | 220 + .../H5P.Dialogcards-1.8/language/pt-br.json | 220 + .../H5P.Dialogcards-1.8/language/pt.json | 220 + .../H5P.Dialogcards-1.8/language/ru.json | 220 + .../H5P.Dialogcards-1.8/language/sl.json | 220 + .../H5P.Dialogcards-1.8/language/sma.json | 220 + .../H5P.Dialogcards-1.8/language/sme.json | 220 + .../H5P.Dialogcards-1.8/language/smj.json | 220 + .../H5P.Dialogcards-1.8/language/sv.json | 220 + .../H5P.Dialogcards-1.8/language/tr.json | 220 + .../H5P.Dialogcards-1.8/language/uk.json | 220 + .../H5P.Dialogcards-1.8/language/vi.json | 220 + .../H5P.Dialogcards-1.8/language/zh.json | 220 + .../H5P.Dialogcards-1.8/library.json | 57 + .../H5P.Dialogcards-1.8/semantics.json | 493 + .../H5P.Dialogcards-1.8/upgrades.js | 41 + .../H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../css/joubel-help-dialog.css | 67 + .../H5P.JoubelUI-1.3/css/joubel-icon.css | 152 + .../css/joubel-message-dialog.css | 25 + .../css/joubel-progress-circle.css | 56 + .../css/joubel-progressbar.css | 38 + .../H5P.JoubelUI-1.3/css/joubel-score-bar.css | 159 + .../css/joubel-simple-rounded-button.css | 28 + .../H5P.JoubelUI-1.3/css/joubel-slider.css | 29 + .../css/joubel-speech-bubble.css | 50 + .../H5P.JoubelUI-1.3/css/joubel-tip.css | 56 + .../H5P.JoubelUI-1.3/css/joubel-ui.css | 96 + .../H5P.JoubelUI-1.3/fonts/joubel.eot | Bin 0 -> 4752 bytes .../H5P.JoubelUI-1.3/fonts/joubel.svg | 30 + .../H5P.JoubelUI-1.3/fonts/joubel.ttf | Bin 0 -> 4592 bytes .../H5P.JoubelUI-1.3/fonts/joubel.woff | Bin 0 -> 4668 bytes .../H5P.JoubelUI-1.3/js/joubel-help-dialog.js | 93 + .../js/joubel-message-dialog.js | 38 + .../js/joubel-progress-circle.js | 159 + .../H5P.JoubelUI-1.3/js/joubel-progressbar.js | 190 + .../H5P.JoubelUI-1.3/js/joubel-score-bar.js | 225 + .../js/joubel-simple-rounded-button.js | 32 + .../H5P.JoubelUI-1.3/js/joubel-slider.js | 96 + .../js/joubel-speech-bubble.js | 356 + .../H5P.JoubelUI-1.3/js/joubel-throbber.js | 19 + .../H5P.JoubelUI-1.3/js/joubel-tip.js | 106 + .../H5P.JoubelUI-1.3/js/joubel-ui.js | 183 + .../H5P.JoubelUI-1.3/library.json | 107 + .../H5P.Transition-1.0/library.json | 16 + .../H5P.Transition-1.0/transition.js | 158 + .../H5PEditor.ShowWhen-1.0/h5p-show-when.css | 3 + .../H5PEditor.ShowWhen-1.0/h5p-show-when.js | 153 + .../H5PEditor.ShowWhen-1.0/library.json | 20 + .../H5PEditor.VerticalTabs-1.3/library.json | 31 + .../styles/css/vertical-tabs.css | 264 + .../vertical-tabs.js | 481 + .../source-dialogcard/Tether-1.0/library.json | 21 + .../Tether-1.0/scripts/tether.min.js | 4 + .../Tether-1.0/styles/tether.min.css | 1 + .../source-dialogcard/content/content.json | 17 + .../content/images/dialogcard.jpg | Bin 0 -> 3806 bytes .../flowplayer-1.0/library.json | 15 + .../scripts/flowplayer-3.2.12.min.js | 24 + .../launch/source-dialogcard/h5p.json | 1 + .../css/drop-theme-arrows-bounce.min.css | 1 + .../Drop-1.0/js/drop.min.js | 7 + .../source-dragthewords/Drop-1.0/library.json | 28 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../H5P.DragText-1.8/dist/h5p-drag-text.css | 237 + .../H5P.DragText-1.8/dist/h5p-drag-text.js | 1 + .../H5P.DragText-1.8/icon.svg | 104 + .../H5P.DragText-1.8/language/af.json | 136 + .../H5P.DragText-1.8/language/ar.json | 136 + .../H5P.DragText-1.8/language/bs.json | 136 + .../H5P.DragText-1.8/language/ca.json | 136 + .../H5P.DragText-1.8/language/cs.json | 136 + .../H5P.DragText-1.8/language/da.json | 136 + .../H5P.DragText-1.8/language/de.json | 136 + .../H5P.DragText-1.8/language/el.json | 136 + .../H5P.DragText-1.8/language/es.json | 136 + .../H5P.DragText-1.8/language/et.json | 136 + .../H5P.DragText-1.8/language/eu.json | 136 + .../H5P.DragText-1.8/language/fi.json | 136 + .../H5P.DragText-1.8/language/fr.json | 136 + .../H5P.DragText-1.8/language/he.json | 136 + .../H5P.DragText-1.8/language/hu.json | 136 + .../H5P.DragText-1.8/language/it.json | 136 + .../H5P.DragText-1.8/language/ja.json | 136 + .../H5P.DragText-1.8/language/ko.json | 136 + .../H5P.DragText-1.8/language/nb.json | 136 + .../H5P.DragText-1.8/language/nl.json | 136 + .../H5P.DragText-1.8/language/nn.json | 136 + .../H5P.DragText-1.8/language/pl.json | 136 + .../H5P.DragText-1.8/language/pt-br.json | 136 + .../H5P.DragText-1.8/language/pt.json | 136 + .../H5P.DragText-1.8/language/ro.json | 136 + .../H5P.DragText-1.8/language/ru.json | 136 + .../H5P.DragText-1.8/language/sl.json | 136 + .../H5P.DragText-1.8/language/sma.json | 136 + .../H5P.DragText-1.8/language/sme.json | 136 + .../H5P.DragText-1.8/language/smj.json | 136 + .../H5P.DragText-1.8/language/sr.json | 136 + .../H5P.DragText-1.8/language/sv.json | 136 + .../H5P.DragText-1.8/language/tr.json | 136 + .../H5P.DragText-1.8/language/uk.json | 136 + .../H5P.DragText-1.8/language/vi.json | 136 + .../H5P.DragText-1.8/language/zh-tw.json | 136 + .../H5P.DragText-1.8/library.json | 57 + .../H5P.DragText-1.8/presave.js | 30 + .../H5P.DragText-1.8/semantics.json | 278 + .../H5P.DragText-1.8/upgrades.js | 60 + .../H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../css/joubel-help-dialog.css | 67 + .../H5P.JoubelUI-1.3/css/joubel-icon.css | 152 + .../css/joubel-message-dialog.css | 25 + .../css/joubel-progress-circle.css | 56 + .../css/joubel-progressbar.css | 38 + .../H5P.JoubelUI-1.3/css/joubel-score-bar.css | 159 + .../css/joubel-simple-rounded-button.css | 28 + .../H5P.JoubelUI-1.3/css/joubel-slider.css | 29 + .../css/joubel-speech-bubble.css | 50 + .../H5P.JoubelUI-1.3/css/joubel-tip.css | 56 + .../H5P.JoubelUI-1.3/css/joubel-ui.css | 96 + .../H5P.JoubelUI-1.3/fonts/joubel.eot | Bin 0 -> 4752 bytes .../H5P.JoubelUI-1.3/fonts/joubel.svg | 30 + .../H5P.JoubelUI-1.3/fonts/joubel.ttf | Bin 0 -> 4592 bytes .../H5P.JoubelUI-1.3/fonts/joubel.woff | Bin 0 -> 4668 bytes .../H5P.JoubelUI-1.3/js/joubel-help-dialog.js | 93 + .../js/joubel-message-dialog.js | 38 + .../js/joubel-progress-circle.js | 159 + .../H5P.JoubelUI-1.3/js/joubel-progressbar.js | 190 + .../H5P.JoubelUI-1.3/js/joubel-score-bar.js | 225 + .../js/joubel-simple-rounded-button.js | 32 + .../H5P.JoubelUI-1.3/js/joubel-slider.js | 96 + .../js/joubel-speech-bubble.js | 356 + .../H5P.JoubelUI-1.3/js/joubel-throbber.js | 19 + .../H5P.JoubelUI-1.3/js/joubel-tip.js | 106 + .../H5P.JoubelUI-1.3/js/joubel-ui.js | 183 + .../H5P.JoubelUI-1.3/library.json | 107 + .../H5P.Question-1.4/images/minus-one.svg | 20 + .../H5P.Question-1.4/images/plus-one.svg | 21 + .../H5P.Question-1.4/library.json | 45 + .../H5P.Question-1.4/scripts/explainer.js | 84 + .../H5P.Question-1.4/scripts/question.js | 1700 ++ .../H5P.Question-1.4/scripts/score-points.js | 72 + .../H5P.Question-1.4/styles/explainer.css | 80 + .../H5P.Question-1.4/styles/question.css | 342 + .../H5P.Transition-1.0/library.json | 16 + .../H5P.Transition-1.0/transition.js | 158 + .../h5p-editor-range-list.css | 61 + .../h5p-editor-range-list.js | 478 + .../H5PEditor.RangeList-1.0/language/de.json | 7 + .../H5PEditor.RangeList-1.0/language/el.json | 7 + .../H5PEditor.RangeList-1.0/language/en.json | 7 + .../language/es-mx.json | 7 + .../H5PEditor.RangeList-1.0/language/es.json | 7 + .../H5PEditor.RangeList-1.0/language/et.json | 7 + .../H5PEditor.RangeList-1.0/language/eu.json | 7 + .../H5PEditor.RangeList-1.0/language/fi.json | 7 + .../H5PEditor.RangeList-1.0/language/fr.json | 7 + .../H5PEditor.RangeList-1.0/language/pl.json | 7 + .../language/pt-br.json | 7 + .../H5PEditor.RangeList-1.0/language/sma.json | 7 + .../H5PEditor.RangeList-1.0/language/sme.json | 7 + .../H5PEditor.RangeList-1.0/language/smj.json | 7 + .../H5PEditor.RangeList-1.0/library.json | 31 + .../h5p-editor-table-list.css | 77 + .../h5p-editor-table-list.js | 248 + .../H5PEditor.TableList-1.0/library.json | 24 + .../Tether-1.0/library.json | 21 + .../Tether-1.0/scripts/tether.min.js | 4 + .../Tether-1.0/styles/tether.min.css | 1 + .../source-dragthewords/content/content.json | 8 + .../launch/source-dragthewords/h5p.json | 1 + .../jQuery.ui-1.10/apply-when-upgrading.diff | 208 + .../jQuery.ui-1.10/h5p-jquery-ui.css | 1174 ++ .../jQuery.ui-1.10/h5p-jquery-ui.js | 15169 ++++++++++++++++ .../images/animated-overlay.gif | Bin 0 -> 1738 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../jQuery.ui-1.10/library.json | 21 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../guess-the-answer.css | 80 + .../guess-the-answer.js | 149 + .../H5P.GuessTheAnswer-1.4/icon.svg | 77 + .../H5P.GuessTheAnswer-1.4/language/bg.json | 26 + .../H5P.GuessTheAnswer-1.4/language/bs.json | 26 + .../H5P.GuessTheAnswer-1.4/language/cs.json | 26 + .../H5P.GuessTheAnswer-1.4/language/de.json | 26 + .../H5P.GuessTheAnswer-1.4/language/el.json | 26 + .../language/es-mx.json | 26 + .../H5P.GuessTheAnswer-1.4/language/es.json | 26 + .../H5P.GuessTheAnswer-1.4/language/et.json | 26 + .../H5P.GuessTheAnswer-1.4/language/eu.json | 26 + .../H5P.GuessTheAnswer-1.4/language/fi.json | 26 + .../H5P.GuessTheAnswer-1.4/language/fr.json | 26 + .../H5P.GuessTheAnswer-1.4/language/it.json | 26 + .../H5P.GuessTheAnswer-1.4/language/nb.json | 26 + .../H5P.GuessTheAnswer-1.4/language/nl.json | 26 + .../H5P.GuessTheAnswer-1.4/language/ru.json | 26 + .../H5P.GuessTheAnswer-1.4/language/sma.json | 26 + .../H5P.GuessTheAnswer-1.4/language/sme.json | 26 + .../H5P.GuessTheAnswer-1.4/language/smj.json | 26 + .../H5P.GuessTheAnswer-1.4/language/sv.json | 26 + .../language/zh-hans.json | 26 + .../H5P.GuessTheAnswer-1.4/library.json | 35 + .../H5P.GuessTheAnswer-1.4/semantics.json | 63 + .../H5P.GuessTheAnswer-1.4/upgrades.js | 48 + .../H5P.Image-1.1/image.css | 9 + .../H5P.Image-1.1/image.js | 77 + .../H5P.Image-1.1/language/af.json | 19 + .../H5P.Image-1.1/language/ar.json | 19 + .../H5P.Image-1.1/language/bg.json | 19 + .../H5P.Image-1.1/language/bs.json | 19 + .../H5P.Image-1.1/language/ca.json | 19 + .../H5P.Image-1.1/language/cs.json | 19 + .../H5P.Image-1.1/language/da.json | 19 + .../H5P.Image-1.1/language/de.json | 19 + .../H5P.Image-1.1/language/el.json | 19 + .../H5P.Image-1.1/language/es-mx.json | 19 + .../H5P.Image-1.1/language/es.json | 19 + .../H5P.Image-1.1/language/et.json | 19 + .../H5P.Image-1.1/language/eu.json | 19 + .../H5P.Image-1.1/language/fi.json | 19 + .../H5P.Image-1.1/language/fr.json | 19 + .../H5P.Image-1.1/language/he.json | 19 + .../H5P.Image-1.1/language/hu.json | 19 + .../H5P.Image-1.1/language/it.json | 19 + .../H5P.Image-1.1/language/ja.json | 19 + .../H5P.Image-1.1/language/ko.json | 19 + .../H5P.Image-1.1/language/nb.json | 19 + .../H5P.Image-1.1/language/nl.json | 19 + .../H5P.Image-1.1/language/nn.json | 19 + .../H5P.Image-1.1/language/pl.json | 19 + .../H5P.Image-1.1/language/pt-br.json | 19 + .../H5P.Image-1.1/language/pt.json | 19 + .../H5P.Image-1.1/language/ro.json | 19 + .../H5P.Image-1.1/language/ru.json | 19 + .../H5P.Image-1.1/language/sl.json | 19 + .../H5P.Image-1.1/language/sma.json | 19 + .../H5P.Image-1.1/language/sme.json | 19 + .../H5P.Image-1.1/language/smj.json | 19 + .../H5P.Image-1.1/language/sr.json | 19 + .../H5P.Image-1.1/language/sv.json | 19 + .../H5P.Image-1.1/language/tr.json | 19 + .../H5P.Image-1.1/language/vi.json | 19 + .../H5P.Image-1.1/language/zh-hans.json | 19 + .../H5P.Image-1.1/language/zh-hant.json | 19 + .../H5P.Image-1.1/library.json | 29 + .../H5P.Image-1.1/placeholder.svg | 25 + .../H5P.Image-1.1/semantics.json | 32 + .../H5P.Image-1.1/upgrades.js | 54 + .../content/content.json | 12 + .../content/images/file-5ef786ac3f86d.jpg | Bin 0 -> 3806 bytes .../launch/source-guesstheanswer/h5p.json | 1 + plugin/h5p/cache-h5p/launch/source-h.html | 19 + .../css/drop-theme-arrows-bounce.min.css | 1 + .../Drop-1.0/js/drop.min.js | 7 + .../source-markthewords/Drop-1.0/library.json | 28 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../css/joubel-help-dialog.css | 67 + .../H5P.JoubelUI-1.3/css/joubel-icon.css | 152 + .../css/joubel-message-dialog.css | 25 + .../css/joubel-progress-circle.css | 56 + .../css/joubel-progressbar.css | 38 + .../H5P.JoubelUI-1.3/css/joubel-score-bar.css | 159 + .../css/joubel-simple-rounded-button.css | 28 + .../H5P.JoubelUI-1.3/css/joubel-slider.css | 29 + .../css/joubel-speech-bubble.css | 50 + .../H5P.JoubelUI-1.3/css/joubel-tip.css | 56 + .../H5P.JoubelUI-1.3/css/joubel-ui.css | 96 + .../H5P.JoubelUI-1.3/fonts/joubel.eot | Bin 0 -> 4752 bytes .../H5P.JoubelUI-1.3/fonts/joubel.svg | 30 + .../H5P.JoubelUI-1.3/fonts/joubel.ttf | Bin 0 -> 4592 bytes .../H5P.JoubelUI-1.3/fonts/joubel.woff | Bin 0 -> 4668 bytes .../H5P.JoubelUI-1.3/js/joubel-help-dialog.js | 93 + .../js/joubel-message-dialog.js | 38 + .../js/joubel-progress-circle.js | 159 + .../H5P.JoubelUI-1.3/js/joubel-progressbar.js | 190 + .../H5P.JoubelUI-1.3/js/joubel-score-bar.js | 225 + .../js/joubel-simple-rounded-button.js | 32 + .../H5P.JoubelUI-1.3/js/joubel-slider.js | 96 + .../js/joubel-speech-bubble.js | 356 + .../H5P.JoubelUI-1.3/js/joubel-throbber.js | 19 + .../H5P.JoubelUI-1.3/js/joubel-tip.js | 106 + .../H5P.JoubelUI-1.3/js/joubel-ui.js | 183 + .../H5P.JoubelUI-1.3/library.json | 107 + .../H5P.MarkTheWords-1.9/icon.svg | 68 + .../H5P.MarkTheWords-1.9/language/af.json | 116 + .../H5P.MarkTheWords-1.9/language/ar.json | 116 + .../H5P.MarkTheWords-1.9/language/bg.json | 116 + .../H5P.MarkTheWords-1.9/language/bs.json | 116 + .../H5P.MarkTheWords-1.9/language/ca.json | 116 + .../H5P.MarkTheWords-1.9/language/cs.json | 116 + .../H5P.MarkTheWords-1.9/language/da.json | 116 + .../H5P.MarkTheWords-1.9/language/de.json | 116 + .../H5P.MarkTheWords-1.9/language/el.json | 116 + .../H5P.MarkTheWords-1.9/language/es-mx.json | 116 + .../H5P.MarkTheWords-1.9/language/es.json | 116 + .../H5P.MarkTheWords-1.9/language/et.json | 116 + .../H5P.MarkTheWords-1.9/language/eu.json | 116 + .../H5P.MarkTheWords-1.9/language/fi.json | 116 + .../H5P.MarkTheWords-1.9/language/fr.json | 116 + .../H5P.MarkTheWords-1.9/language/he.json | 116 + .../H5P.MarkTheWords-1.9/language/hu.json | 116 + .../H5P.MarkTheWords-1.9/language/it.json | 116 + .../H5P.MarkTheWords-1.9/language/ja.json | 116 + .../H5P.MarkTheWords-1.9/language/ko.json | 116 + .../H5P.MarkTheWords-1.9/language/nb.json | 116 + .../H5P.MarkTheWords-1.9/language/nl.json | 116 + .../H5P.MarkTheWords-1.9/language/nn.json | 116 + .../H5P.MarkTheWords-1.9/language/pl.json | 116 + .../H5P.MarkTheWords-1.9/language/pt-br.json | 116 + .../H5P.MarkTheWords-1.9/language/pt.json | 116 + .../H5P.MarkTheWords-1.9/language/ro.json | 116 + .../H5P.MarkTheWords-1.9/language/ru.json | 116 + .../H5P.MarkTheWords-1.9/language/sl.json | 116 + .../H5P.MarkTheWords-1.9/language/sma.json | 116 + .../H5P.MarkTheWords-1.9/language/sme.json | 116 + .../H5P.MarkTheWords-1.9/language/smj.json | 116 + .../H5P.MarkTheWords-1.9/language/sr.json | 116 + .../H5P.MarkTheWords-1.9/language/sv.json | 116 + .../H5P.MarkTheWords-1.9/language/tr.json | 116 + .../H5P.MarkTheWords-1.9/language/uk.json | 116 + .../H5P.MarkTheWords-1.9/language/vi.json | 116 + .../H5P.MarkTheWords-1.9/language/zh.json | 116 + .../H5P.MarkTheWords-1.9/library.json | 61 + .../H5P.MarkTheWords-1.9/presave.js | 31 + .../scripts/keyboard-nav.js | 268 + .../scripts/mark-the-words.js | 653 + .../H5P.MarkTheWords-1.9/scripts/word.js | 229 + .../scripts/xAPI-generator.js | 132 + .../H5P.MarkTheWords-1.9/semantics.json | 244 + .../styles/mark-the-words.css | 112 + .../H5P.MarkTheWords-1.9/upgrades.js | 67 + .../H5P.Question-1.4/images/minus-one.svg | 20 + .../H5P.Question-1.4/images/plus-one.svg | 21 + .../H5P.Question-1.4/library.json | 45 + .../H5P.Question-1.4/scripts/explainer.js | 84 + .../H5P.Question-1.4/scripts/question.js | 1698 ++ .../H5P.Question-1.4/scripts/score-points.js | 72 + .../H5P.Question-1.4/styles/explainer.css | 80 + .../H5P.Question-1.4/styles/question.css | 342 + .../H5P.Transition-1.0/library.json | 16 + .../H5P.Transition-1.0/transition.js | 158 + .../h5p-editor-range-list.css | 61 + .../h5p-editor-range-list.js | 478 + .../H5PEditor.RangeList-1.0/language/bg.json | 7 + .../H5PEditor.RangeList-1.0/language/cs.json | 7 + .../H5PEditor.RangeList-1.0/language/de.json | 7 + .../H5PEditor.RangeList-1.0/language/el.json | 7 + .../H5PEditor.RangeList-1.0/language/en.json | 7 + .../language/es-mx.json | 7 + .../H5PEditor.RangeList-1.0/language/es.json | 7 + .../H5PEditor.RangeList-1.0/language/et.json | 7 + .../H5PEditor.RangeList-1.0/language/eu.json | 7 + .../H5PEditor.RangeList-1.0/language/fi.json | 7 + .../H5PEditor.RangeList-1.0/language/fr.json | 7 + .../H5PEditor.RangeList-1.0/language/it.json | 7 + .../H5PEditor.RangeList-1.0/language/pl.json | 7 + .../language/pt-br.json | 7 + .../H5PEditor.RangeList-1.0/language/sma.json | 7 + .../H5PEditor.RangeList-1.0/language/sme.json | 7 + .../H5PEditor.RangeList-1.0/language/smj.json | 7 + .../H5PEditor.RangeList-1.0/library.json | 31 + .../h5p-editor-table-list.css | 77 + .../h5p-editor-table-list.js | 248 + .../H5PEditor.TableList-1.0/library.json | 24 + .../Tether-1.0/library.json | 21 + .../Tether-1.0/scripts/tether.min.js | 4 + .../Tether-1.0/styles/tether.min.css | 1 + .../source-markthewords/content/content.json | 16 + .../launch/source-markthewords/h5p.json | 1 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../source-memory/H5P.MemoryGame-1.3/card.js | 408 + .../H5P.MemoryGame-1.3/counter.js | 39 + .../source-memory/H5P.MemoryGame-1.3/icon.svg | 86 + .../H5P.MemoryGame-1.3/language/af.json | 121 + .../H5P.MemoryGame-1.3/language/ar.json | 121 + .../H5P.MemoryGame-1.3/language/bs.json | 121 + .../H5P.MemoryGame-1.3/language/ca.json | 121 + .../H5P.MemoryGame-1.3/language/cs.json | 121 + .../H5P.MemoryGame-1.3/language/da.json | 121 + .../H5P.MemoryGame-1.3/language/de.json | 121 + .../H5P.MemoryGame-1.3/language/el.json | 121 + .../H5P.MemoryGame-1.3/language/es-mx.json | 121 + .../H5P.MemoryGame-1.3/language/es.json | 121 + .../H5P.MemoryGame-1.3/language/et.json | 121 + .../H5P.MemoryGame-1.3/language/eu.json | 121 + .../H5P.MemoryGame-1.3/language/fi.json | 121 + .../H5P.MemoryGame-1.3/language/fr.json | 121 + .../H5P.MemoryGame-1.3/language/he.json | 121 + .../H5P.MemoryGame-1.3/language/hu.json | 121 + .../H5P.MemoryGame-1.3/language/it.json | 121 + .../H5P.MemoryGame-1.3/language/ja.json | 121 + .../H5P.MemoryGame-1.3/language/ko.json | 121 + .../H5P.MemoryGame-1.3/language/nb.json | 121 + .../H5P.MemoryGame-1.3/language/nl.json | 121 + .../H5P.MemoryGame-1.3/language/nn.json | 121 + .../H5P.MemoryGame-1.3/language/pl.json | 121 + .../H5P.MemoryGame-1.3/language/pt-br.json | 121 + .../H5P.MemoryGame-1.3/language/pt.json | 121 + .../H5P.MemoryGame-1.3/language/ro.json | 121 + .../H5P.MemoryGame-1.3/language/ru.json | 121 + .../H5P.MemoryGame-1.3/language/sl.json | 121 + .../H5P.MemoryGame-1.3/language/sma.json | 121 + .../H5P.MemoryGame-1.3/language/sme.json | 121 + .../H5P.MemoryGame-1.3/language/smj.json | 121 + .../H5P.MemoryGame-1.3/language/sr.json | 121 + .../H5P.MemoryGame-1.3/language/sv.json | 121 + .../H5P.MemoryGame-1.3/language/tr.json | 121 + .../H5P.MemoryGame-1.3/language/uk.json | 121 + .../H5P.MemoryGame-1.3/language/vi.json | 121 + .../H5P.MemoryGame-1.3/language/zh-hans.json | 121 + .../H5P.MemoryGame-1.3/language/zh-hant.json | 121 + .../H5P.MemoryGame-1.3/language/zh-tw.json | 121 + .../H5P.MemoryGame-1.3/library.json | 62 + .../H5P.MemoryGame-1.3/memory-game.css | 345 + .../H5P.MemoryGame-1.3/memory-game.js | 565 + .../source-memory/H5P.MemoryGame-1.3/popup.js | 79 + .../H5P.MemoryGame-1.3/semantics.json | 228 + .../source-memory/H5P.MemoryGame-1.3/timer.js | 55 + .../H5P.MemoryGame-1.3/upgrades.js | 45 + .../source-memory/H5P.Timer-0.4/library.json | 15 + .../H5P.Timer-0.4/scripts/timer.js | 707 + .../dist/h5p-editor-audio-recorder.js | 2 + .../language/de.json | 23 + .../language/el.json | 23 + .../language/en.json | 23 + .../language/et.json | 23 + .../language/eu.json | 23 + .../language/nb.json | 23 + .../language/sma.json | 23 + .../language/sme.json | 23 + .../language/smj.json | 23 + .../language/zh.json | 23 + .../H5PEditor.AudioRecorder-1.0/library.json | 32 + .../H5PEditor.ColorSelector-1.2/library.json | 29 + .../scripts/color-selector.js | 110 + .../scripts/spectrum.js | 2327 +++ .../styles/color-selector.css | 21 + .../styles/spectrum.css | 507 + .../H5PEditor.VerticalTabs-1.3/library.json | 31 + .../styles/css/vertical-tabs.css | 264 + .../vertical-tabs.js | 481 + .../launch/source-memory/content/content.json | 3 + .../content/images/image-5e3b1b2ec709c.jpg | Bin 0 -> 3726 bytes .../content/images/image-5e3b1b45cadc3.jpg | Bin 0 -> 23819 bytes .../content/images/image-5e3b1b52d7174.jpg | Bin 0 -> 19884 bytes .../cache-h5p/launch/source-memory/h5p.json | 1 + .../css/drop-theme-arrows-bounce.min.css | 1 + .../source-wordsmatch/Drop-1.0/js/drop.min.js | 7 + .../source-wordsmatch/Drop-1.0/library.json | 28 + .../FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../FontAwesome-4.5/library.json | 16 + .../H5P.FindTheWords-1.4/icon.svg | 106 + .../H5P.FindTheWords-1.4/library.json | 53 + .../scripts/h5p-find-the-words-counter.js | 35 + .../scripts/h5p-find-the-words-timer.js | 47 + .../scripts/h5p-find-the-words-vocabulary.js | 137 + .../scripts/h5p-find-the-words-word-grid.js | 788 + .../scripts/h5p-find-the-words.js | 468 + .../H5P.FindTheWords-1.4/semantics.json | 187 + .../styles/h5p-find-the-words.css | 176 + .../H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../css/joubel-help-dialog.css | 67 + .../H5P.JoubelUI-1.3/css/joubel-icon.css | 152 + .../css/joubel-message-dialog.css | 25 + .../css/joubel-progress-circle.css | 56 + .../css/joubel-progressbar.css | 38 + .../H5P.JoubelUI-1.3/css/joubel-score-bar.css | 159 + .../css/joubel-simple-rounded-button.css | 28 + .../H5P.JoubelUI-1.3/css/joubel-slider.css | 29 + .../css/joubel-speech-bubble.css | 50 + .../H5P.JoubelUI-1.3/css/joubel-tip.css | 56 + .../H5P.JoubelUI-1.3/css/joubel-ui.css | 96 + .../H5P.JoubelUI-1.3/fonts/joubel.eot | Bin 0 -> 4752 bytes .../H5P.JoubelUI-1.3/fonts/joubel.svg | 30 + .../H5P.JoubelUI-1.3/fonts/joubel.ttf | Bin 0 -> 4592 bytes .../H5P.JoubelUI-1.3/fonts/joubel.woff | Bin 0 -> 4668 bytes .../H5P.JoubelUI-1.3/js/joubel-help-dialog.js | 93 + .../js/joubel-message-dialog.js | 38 + .../js/joubel-progress-circle.js | 159 + .../H5P.JoubelUI-1.3/js/joubel-progressbar.js | 190 + .../H5P.JoubelUI-1.3/js/joubel-score-bar.js | 225 + .../js/joubel-simple-rounded-button.js | 32 + .../H5P.JoubelUI-1.3/js/joubel-slider.js | 96 + .../js/joubel-speech-bubble.js | 356 + .../H5P.JoubelUI-1.3/js/joubel-throbber.js | 19 + .../H5P.JoubelUI-1.3/js/joubel-tip.js | 106 + .../H5P.JoubelUI-1.3/js/joubel-ui.js | 183 + .../H5P.JoubelUI-1.3/library.json | 107 + .../H5P.Timer-0.4/library.json | 15 + .../H5P.Timer-0.4/scripts/timer.js | 707 + .../H5P.Transition-1.0/library.json | 16 + .../H5P.Transition-1.0/transition.js | 158 + .../source-wordsmatch/Tether-1.0/library.json | 21 + .../Tether-1.0/scripts/tether.min.js | 4 + .../Tether-1.0/styles/tether.min.css | 1 + .../source-wordsmatch/content/content.json | 6 + .../launch/source-wordsmatch/h5p.json | 1 + .../cache-h5p/launch/test-mark-the-words.html | 23 + plugin/h5p/cache-h5p/launch/wordsmatch.html | 23 + plugin/h5p/cache-h5p/launch/workspace.html | 23 + .../css/drop-theme-arrows-bounce.min.css | 1 + .../launch/workspace/Drop-1.0/js/drop.min.js | 7 + .../launch/workspace/Drop-1.0/library.json | 28 + .../workspace/FontAwesome-4.5/FontAwesome.otf | Bin 0 -> 109688 bytes .../FontAwesome-4.5/fontawesome-webfont.eot | Bin 0 -> 70807 bytes .../FontAwesome-4.5/fontawesome-webfont.svg | 655 + .../FontAwesome-4.5/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../FontAwesome-4.5/fontawesome-webfont.woff | Bin 0 -> 83588 bytes .../FontAwesome-4.5/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes .../FontAwesome-4.5/h5p-font-awesome.min.css | 4 + .../workspace/FontAwesome-4.5/library.json | 16 + .../workspace/H5P.FontIcons-1.0/fonts/h5p.eot | Bin 0 -> 16800 bytes .../workspace/H5P.FontIcons-1.0/fonts/h5p.svg | 93 + .../workspace/H5P.FontIcons-1.0/fonts/h5p.ttf | Bin 0 -> 16652 bytes .../H5P.FontIcons-1.0/fonts/h5p.woff | Bin 0 -> 16728 bytes .../workspace/H5P.FontIcons-1.0/library.json | 14 + .../styles/h5p-font-icons.css | 10 + .../css/joubel-help-dialog.css | 67 + .../H5P.JoubelUI-1.3/css/joubel-icon.css | 152 + .../css/joubel-message-dialog.css | 25 + .../css/joubel-progress-circle.css | 56 + .../css/joubel-progressbar.css | 38 + .../H5P.JoubelUI-1.3/css/joubel-score-bar.css | 159 + .../css/joubel-simple-rounded-button.css | 28 + .../H5P.JoubelUI-1.3/css/joubel-slider.css | 29 + .../css/joubel-speech-bubble.css | 50 + .../H5P.JoubelUI-1.3/css/joubel-tip.css | 56 + .../H5P.JoubelUI-1.3/css/joubel-ui.css | 96 + .../H5P.JoubelUI-1.3/fonts/joubel.eot | Bin 0 -> 4752 bytes .../H5P.JoubelUI-1.3/fonts/joubel.svg | 30 + .../H5P.JoubelUI-1.3/fonts/joubel.ttf | Bin 0 -> 4592 bytes .../H5P.JoubelUI-1.3/fonts/joubel.woff | Bin 0 -> 4668 bytes .../H5P.JoubelUI-1.3/js/joubel-help-dialog.js | 93 + .../js/joubel-message-dialog.js | 38 + .../js/joubel-progress-circle.js | 159 + .../H5P.JoubelUI-1.3/js/joubel-progressbar.js | 190 + .../H5P.JoubelUI-1.3/js/joubel-score-bar.js | 225 + .../js/joubel-simple-rounded-button.js | 32 + .../H5P.JoubelUI-1.3/js/joubel-slider.js | 96 + .../js/joubel-speech-bubble.js | 356 + .../H5P.JoubelUI-1.3/js/joubel-throbber.js | 19 + .../H5P.JoubelUI-1.3/js/joubel-tip.js | 106 + .../H5P.JoubelUI-1.3/js/joubel-ui.js | 183 + .../workspace/H5P.JoubelUI-1.3/library.json | 107 + .../H5P.Question-1.4/images/minus-one.svg | 20 + .../H5P.Question-1.4/images/plus-one.svg | 21 + .../workspace/H5P.Question-1.4/library.json | 45 + .../H5P.Question-1.4/scripts/explainer.js | 84 + .../H5P.Question-1.4/scripts/question.js | 1700 ++ .../H5P.Question-1.4/scripts/score-points.js | 72 + .../H5P.Question-1.4/styles/explainer.css | 80 + .../H5P.Question-1.4/styles/question.css | 342 + .../workspace/H5P.Transition-1.0/library.json | 16 + .../H5P.Transition-1.0/transition.js | 158 + .../workspace/H5P.TrueFalse-1.6/icon.svg | 47 + .../H5P.TrueFalse-1.6/language/af.json | 147 + .../H5P.TrueFalse-1.6/language/ar.json | 147 + .../H5P.TrueFalse-1.6/language/bs.json | 147 + .../H5P.TrueFalse-1.6/language/ca.json | 147 + .../H5P.TrueFalse-1.6/language/cs.json | 147 + .../H5P.TrueFalse-1.6/language/da.json | 147 + .../H5P.TrueFalse-1.6/language/de.json | 147 + .../H5P.TrueFalse-1.6/language/el.json | 147 + .../H5P.TrueFalse-1.6/language/es.json | 147 + .../H5P.TrueFalse-1.6/language/et.json | 147 + .../H5P.TrueFalse-1.6/language/eu.json | 147 + .../H5P.TrueFalse-1.6/language/fi.json | 147 + .../H5P.TrueFalse-1.6/language/fr.json | 147 + .../H5P.TrueFalse-1.6/language/he.json | 147 + .../H5P.TrueFalse-1.6/language/hu.json | 147 + .../H5P.TrueFalse-1.6/language/it.json | 147 + .../H5P.TrueFalse-1.6/language/ja.json | 147 + .../H5P.TrueFalse-1.6/language/ko.json | 147 + .../H5P.TrueFalse-1.6/language/nb.json | 147 + .../H5P.TrueFalse-1.6/language/nl.json | 147 + .../H5P.TrueFalse-1.6/language/nn.json | 147 + .../H5P.TrueFalse-1.6/language/pl.json | 147 + .../H5P.TrueFalse-1.6/language/pt.json | 147 + .../H5P.TrueFalse-1.6/language/ro.json | 147 + .../H5P.TrueFalse-1.6/language/ru.json | 147 + .../H5P.TrueFalse-1.6/language/sr.json | 147 + .../H5P.TrueFalse-1.6/language/sv.json | 147 + .../H5P.TrueFalse-1.6/language/tr.json | 147 + .../H5P.TrueFalse-1.6/language/uk.json | 147 + .../H5P.TrueFalse-1.6/language/vi.json | 147 + .../H5P.TrueFalse-1.6/language/zh-hans.json | 147 + .../H5P.TrueFalse-1.6/language/zh-hant.json | 147 + .../workspace/H5P.TrueFalse-1.6/library.json | 68 + .../workspace/H5P.TrueFalse-1.6/presave.js | 26 + .../scripts/h5p-true-false-answer-group.js | 186 + .../scripts/h5p-true-false-answer.js | 233 + .../scripts/h5p-true-false.js | 483 + .../H5P.TrueFalse-1.6/semantics.json | 309 + .../styles/h5p-true-false.css | 87 + .../workspace/H5P.TrueFalse-1.6/upgrades.js | 42 + .../H5PEditor.RadioGroup-1.1/library.json | 23 + .../H5PEditor.RadioGroup-1.1/radio-group.css | 10 + .../H5PEditor.RadioGroup-1.1/radio-group.js | 117 + .../H5PEditor.ShowWhen-1.0/h5p-show-when.css | 3 + .../H5PEditor.ShowWhen-1.0/h5p-show-when.js | 153 + .../H5PEditor.ShowWhen-1.0/library.json | 20 + .../launch/workspace/H5P_CONTENT_HERE.txt | 0 .../launch/workspace/Tether-1.0/library.json | 21 + .../Tether-1.0/scripts/tether.min.js | 4 + .../Tether-1.0/styles/tether.min.css | 1 + .../launch/workspace/content/content.json | 1 + .../h5p/cache-h5p/launch/workspace/h5p.json | 1 + plugin/h5p/cache-h5p/package.json | 47 + plugin/h5p/cache-h5p/src/js/frame.js | 38 + .../h5p/cache-h5p/src/js/h5p-integration.js | 75 + .../cache-h5p/src/js/h5p-standalone.class.js | 208 + plugin/h5p/cache-h5p/src/js/index.js | 10 + plugin/h5p/cache-h5p/webpack.common.js | 72 + plugin/h5p/cache-h5p/webpack.config.js | 3 + plugin/h5p/cache-h5p/webpack.dev.js | 11 + plugin/h5p/cache-h5p/webpack.prod.js | 6 + plugin/h5p/h5p_plugin.class.php | 62 + plugin/h5p/inc/action.switch.php | 75 + plugin/h5p/inc/edit.form.php | 97 + plugin/h5p/inc/translate.php | 34 + plugin/h5p/index.php | 28 + plugin/h5p/install.php | 10 + plugin/h5p/lang/english.php | 41 + plugin/h5p/lang/french.php | 41 + plugin/h5p/lang/spanish.php | 41 + plugin/h5p/list.php | 139 + plugin/h5p/node_process.php | 287 + plugin/h5p/plugin.php | 6 + plugin/h5p/resources/ajax/getnodes.php | 33 + plugin/h5p/resources/css/H5P.css | 162 + plugin/h5p/resources/css/demo.css | 68 + plugin/h5p/resources/css/window-h5p.css | 49 + plugin/h5p/resources/img/add.png | Bin 0 -> 1302 bytes plugin/h5p/resources/img/delete.png | Bin 0 -> 1221 bytes plugin/h5p/resources/img/dialogcard.jpg | Bin 0 -> 3806 bytes plugin/h5p/resources/img/dialogcard.png | Bin 0 -> 1827 bytes plugin/h5p/resources/img/dragthewords.png | Bin 0 -> 5214 bytes plugin/h5p/resources/img/edit.png | Bin 0 -> 591 bytes plugin/h5p/resources/img/guesstheanswer.png | Bin 0 -> 8731 bytes plugin/h5p/resources/img/h5pchami.png | Bin 0 -> 605 bytes plugin/h5p/resources/img/loadtable.gif | Bin 0 -> 58255 bytes plugin/h5p/resources/img/markthewords.png | Bin 0 -> 5328 bytes plugin/h5p/resources/img/memory.png | Bin 0 -> 1507 bytes plugin/h5p/resources/img/wordsmatch.png | Bin 0 -> 2760 bytes plugin/h5p/resources/js/h5p_extras.js | 206 + plugin/h5p/resources/js/interface.js | 654 + .../h5p/resources/js/jquery.dataTables.min.js | 164 + plugin/h5p/resources/js/pell.js | 138 + plugin/h5p/resources/js/pell.min.css | 6 + plugin/h5p/resources/js/pell.min.js | 1 + plugin/h5p/uninstall.php | 8 + plugin/h5p/update.php | 31 + plugin/h5p/view/list.tpl | 59 + plugin/h5p/view/update.tpl | 2 + plugin/h5p/view/view.tpl | 3 + plugin/h5pimport/Entity/H5pImport.php | 255 + plugin/h5pimport/Entity/H5pImportLibrary.php | 383 + plugin/h5pimport/Entity/H5pImportResults.php | 267 + plugin/h5pimport/H5pImportPlugin.php | 324 + plugin/h5pimport/LICENSE.txt | 674 + plugin/h5pimport/README.md | 40 + plugin/h5pimport/config.php | 5 + plugin/h5pimport/install.php | 8 + plugin/h5pimport/lang/english.php | 23 + plugin/h5pimport/lang/french.php | 23 + plugin/h5pimport/lang/spanish.php | 23 + plugin/h5pimport/plugin.php | 5 + plugin/h5pimport/src/H5pImplementation.php | 395 + plugin/h5pimport/src/H5pPackageImporter.php | 78 + plugin/h5pimport/src/H5pPackageTools.php | 327 + .../H5pImportCreateCourseHookObserver.php | 34 + plugin/h5pimport/src/ZipPackageImporter.php | 183 + plugin/h5pimport/src/ajax.php | 90 + plugin/h5pimport/start.php | 240 + plugin/h5pimport/uninstall.php | 6 + plugin/h5pimport/view.php | 189 + plugin/h5pimport/view/index.tpl | 6 + plugin/hello_world/README.md | 6 + plugin/hello_world/index.php | 18 + plugin/hello_world/install.php | 14 + plugin/hello_world/lang/english.php | 7 + plugin/hello_world/lang/french.php | 3 + plugin/hello_world/lang/spanish.php | 3 + plugin/hello_world/plugin.php | 41 + plugin/hello_world/uninstall.php | 2 + plugin/ims_lti/.htaccess | 1 + plugin/ims_lti/Entity/ImsLtiTool.php | 930 + plugin/ims_lti/Entity/LineItem.php | 241 + plugin/ims_lti/Entity/Platform.php | 112 + plugin/ims_lti/Entity/Token.php | 187 + plugin/ims_lti/ImsLtiPlugin.php | 755 + plugin/ims_lti/README.md | 241 + plugin/ims_lti/admin.php | 56 + plugin/ims_lti/ags2.php | 51 + plugin/ims_lti/assets/style.css | 37 + plugin/ims_lti/auth.php | 314 + plugin/ims_lti/configure.php | 274 + plugin/ims_lti/create.php | 115 + plugin/ims_lti/delete.php | 43 + plugin/ims_lti/edit.php | 145 + plugin/ims_lti/form.php | 156 + plugin/ims_lti/gradebook/OutcomeForm.php | 190 + plugin/ims_lti/gradebook/add_eval.php | 145 + plugin/ims_lti/install.php | 13 + plugin/ims_lti/item_return.php | 74 + plugin/ims_lti/item_return2.php | 85 + plugin/ims_lti/jwks.php | 47 + plugin/ims_lti/lang/english.php | 77 + plugin/ims_lti/lang/french.php | 77 + plugin/ims_lti/lang/spanish.php | 77 + plugin/ims_lti/login.php | 44 + plugin/ims_lti/multiply.php | 149 + plugin/ims_lti/multiply_session.php | 163 + plugin/ims_lti/nrps2.php | 51 + plugin/ims_lti/outcome_service.php | 69 + plugin/ims_lti/platform.php | 39 + plugin/ims_lti/plugin.php | 4 + plugin/ims_lti/session.php | 91 + .../src/ContentItem/LtiContentItemType.php | 28 + .../src/ContentItem/LtiResourceLink.php | 150 + plugin/ims_lti/src/Form/FrmAdd.php | 239 + plugin/ims_lti/src/Form/FrmEdit.php | 224 + plugin/ims_lti/src/ImsLti.php | 240 + .../src/ImsLtiServiceDeleteRequest.php | 72 + .../src/ImsLtiServiceDeleteResponse.php | 25 + .../ims_lti/src/ImsLtiServiceReadRequest.php | 81 + .../ims_lti/src/ImsLtiServiceReadResponse.php | 31 + .../src/ImsLtiServiceReplaceRequest.php | 101 + .../src/ImsLtiServiceReplaceResponse.php | 25 + plugin/ims_lti/src/ImsLtiServiceRequest.php | 80 + .../src/ImsLtiServiceRequestFactory.php | 35 + plugin/ims_lti/src/ImsLtiServiceResponse.php | 60 + .../src/ImsLtiServiceResponseFactory.php | 30 + .../src/ImsLtiServiceResponseStatus.php | 162 + .../src/ImsLtiServiceUnsupportedRequest.php | 30 + .../src/ImsLtiServiceUnsupportedResponse.php | 24 + plugin/ims_lti/src/LtiAuthException.php | 149 + .../ims_lti/src/Request/LtiTokenRequest.php | 117 + .../src/Service/LtiAdvantageService.php | 49 + .../Service/LtiAssignmentGradesService.php | 155 + .../LtiNamesRoleProvisioningService.php | 86 + .../Resource/LtiAdvantageServiceResource.php | 123 + .../Resource/LtiContextMembershipResource.php | 291 + .../Service/Resource/LtiLineItemResource.php | 230 + .../Service/Resource/LtiLineItemsResource.php | 265 + .../Service/Resource/LtiResultsResource.php | 235 + .../Service/Resource/LtiScoresResource.php | 214 + plugin/ims_lti/start.php | 50 + plugin/ims_lti/token.php | 72 + plugin/ims_lti/tool_settings.php | 66 + plugin/ims_lti/uninstall.php | 13 + plugin/ims_lti/vendor/oauth1/.gitignore | 42 + plugin/ims_lti/vendor/oauth1/CHANGELOG.md | 44 + plugin/ims_lti/vendor/oauth1/LICENSE.txt | 22 + plugin/ims_lti/vendor/oauth1/README.md | 6 + .../vendor/oauth1/code/OAuthConsumer.php | 16 + .../vendor/oauth1/code/OAuthDataStore.php | 27 + .../vendor/oauth1/code/OAuthException.php | 7 + .../vendor/oauth1/code/OAuthRequest.php | 260 + .../vendor/oauth1/code/OAuthServer.php | 223 + .../oauth1/code/OAuthSignatureMethod.php | 55 + .../code/OAuthSignatureMethod_HMAC_SHA1.php | 30 + .../code/OAuthSignatureMethod_PLAINTEXT.php | 34 + .../code/OAuthSignatureMethod_RSA_SHA1.php | 69 + .../ims_lti/vendor/oauth1/code/OAuthTests.xml | 13 + .../ims_lti/vendor/oauth1/code/OAuthToken.php | 31 + .../ims_lti/vendor/oauth1/code/OAuthUtil.php | 153 + .../vendor/oauth1/code/OAuth_TestServer.php | 106 + plugin/ims_lti/vendor/oauth1/composer.json | 30 + plugin/ims_lti/vendor/oauth1/composer.lock | 20 + plugin/ims_lti/vendor/oauth1/doc/design.md | 73 + .../oauth1/example/SimpleOAuthDataStore.php | 74 + .../vendor/oauth1/example/access_token.php | 14 + .../ims_lti/vendor/oauth1/example/client.php | 133 + .../vendor/oauth1/example/common.inc.php | 26 + .../vendor/oauth1/example/echo_api.php | 21 + .../ims_lti/vendor/oauth1/example/index.php | 108 + .../vendor/oauth1/example/request_token.php | 14 + plugin/ims_lti/vendor/oauth1/init.php | 5 + .../tests/Mock_OAuthBaseStringRequest.php | 12 + .../oauth1/tests/Mock_OAuthDataStore.php | 57 + .../Mock_OAuthSignatureMethod_RSA_SHA1.php | 47 + .../vendor/oauth1/tests/OAuthConsumerTest.php | 10 + .../vendor/oauth1/tests/OAuthRequestTest.php | 329 + .../vendor/oauth1/tests/OAuthServerTest.php | 225 + .../OAuthSignatureMethodHmacSha1Test.php | 60 + .../OAuthSignatureMethodPlaintextTest.php | 79 + .../tests/OAuthSignatureMethodRsaSha1Test.php | 43 + .../vendor/oauth1/tests/OAuthTokenTest.php | 20 + .../vendor/oauth1/tests/OAuthUtilTest.php | 151 + plugin/ims_lti/vendor/oauth1/tests/common.php | 60 + plugin/ims_lti/view/add.tpl | 68 + plugin/ims_lti/view/admin.tpl | 104 + plugin/ims_lti/view/start.tpl | 6 + plugin/index.html | 8 + plugin/jcapture/JCaptureApplet.html | 9 + plugin/jcapture/README.md | 12 + plugin/jcapture/action.php | 52 + plugin/jcapture/applet.php | 95 + plugin/jcapture/camera.png | Bin 0 -> 164 bytes plugin/jcapture/jcapture.jnlp | 21 + plugin/jcapture/lib/apache-mime4j-0.6.jar | Bin 0 -> 371003 bytes plugin/jcapture/lib/commons-codec-1.3.jar | Bin 0 -> 54208 bytes plugin/jcapture/lib/commons-logging-1.1.1.jar | Bin 0 -> 68175 bytes plugin/jcapture/lib/httpclient-4.0.1.jar | Bin 0 -> 320260 bytes plugin/jcapture/lib/httpcore-4.0.1.jar | Bin 0 -> 194080 bytes plugin/jcapture/lib/httpmime-4.0.1.jar | Bin 0 -> 31605 bytes plugin/jcapture/lib/jcapture.jar | Bin 0 -> 167769 bytes plugin/jcapture/lib/transform-3.0.2.jar | Bin 0 -> 562414 bytes plugin/jcapture/license.txt | 1 + plugin/jcapture/plugin.php | 12 + plugin/jcapture/plugin_applet.php | 84 + plugin/jcapture/script.js | 19 + .../com.hammurapi.jcapture.VideoEncoder | 1 + .../jcapture/AWTUtilitiesTranslucener.java | 17 + .../jcapture/AbstractCaptureApplet.java | 275 + .../com/hammurapi/jcapture/ButtonManager.java | 85 + .../com/hammurapi/jcapture/CaptureConfig.java | 370 + .../com/hammurapi/jcapture/CaptureFrame.java | 405 + .../jcapture/CaptureOptionsDialog.java | 606 + .../hammurapi/jcapture/ComponentMover.java | 361 + .../hammurapi/jcapture/ComponentResizer.java | 446 + .../com/hammurapi/jcapture/FragmentImpl.java | 28 + .../src/com/hammurapi/jcapture/FrameImpl.java | 57 + .../jcapture/GraphicsDeviceTranslucener.java | 21 + .../com/hammurapi/jcapture/ImageToolTip.java | 80 + .../hammurapi/jcapture/JCaptureApplet.java | 92 + .../com/hammurapi/jcapture/MappedImage.java | 121 + .../src/com/hammurapi/jcapture/Movie.java | 55 + .../hammurapi/jcapture/MovieEditorDialog.java | 1124 ++ .../jcapture/RecordingControlsFrame.java | 321 + .../src/com/hammurapi/jcapture/Region.java | 221 + .../hammurapi/jcapture/ScreenRecorder.java | 312 + .../com/hammurapi/jcapture/ScreenShot.java | 251 + .../src/com/hammurapi/jcapture/ShapeImpl.java | 75 + .../com/hammurapi/jcapture/SwfEncoder.java | 353 + .../com/hammurapi/jcapture/Translucener.java | 17 + .../com/hammurapi/jcapture/VideoEncoder.java | 164 + .../src/com/hammurapi/jcapture/WavFile.java | 787 + .../hammurapi/jcapture/WavFileException.java | 26 + .../src/com/hammurapi/jcapture/camera.png | Bin 0 -> 164 bytes .../src/com/hammurapi/jcapture/mouse.png | Bin 0 -> 519 bytes .../hammurapi/jcapture/toolbar_buttons.swf | Bin 0 -> 2124 bytes plugin/jcapture/upload.php | 62 + plugin/justification/Justification.php | 101 + plugin/justification/README.md | 39 + plugin/justification/add.php | 68 + plugin/justification/cron.php | 88 + plugin/justification/edit.php | 76 + plugin/justification/index.php | 4 + plugin/justification/install.php | 8 + .../justification/justification_by_user.php | 114 + plugin/justification/lang/english.php | 27 + plugin/justification/lang/french.php | 27 + plugin/justification/lang/spanish.php | 27 + plugin/justification/list.php | 66 + plugin/justification/plugin.php | 6 + plugin/justification/set_course.php | 57 + plugin/justification/uninstall.php | 8 + plugin/justification/view/add.tpl | 48 + .../view/justification_user_list.tpl | 38 + plugin/justification/view/list.tpl | 33 + plugin/kannelsms/README.md | 10 + plugin/kannelsms/config.php | 15 + plugin/kannelsms/index.php | 1 + plugin/kannelsms/install.php | 16 + plugin/kannelsms/lang/english.php | 105 + plugin/kannelsms/lang/spanish.php | 105 + plugin/kannelsms/lib/kannelsms.lib.php | 661 + .../kannelsms/lib/kannelsms_plugin.class.php | 56 + plugin/kannelsms/license.txt | 5 + plugin/kannelsms/plugin.php | 11 + .../account_approved_connect.tpl | 1 + ...account_created_updated_login_password.tpl | 1 + .../answer_open_question_test_reviewed.tpl | 1 + .../assignment_been_created_course.tpl | 1 + .../been_invited_complete_survey_course.tpl | 1 + .../sms_templates/been_invited_join_group.tpl | 1 + .../sms_templates/been_subscribed_course.tpl | 1 + .../check_email_connect_more_info.tpl | 1 + ...course_opening_request_code_registered.tpl | 1 + .../course_opening_request_course_code.tpl | 1 + ...e_opening_request_course_code_approved.tpl | 1 + .../messages_sent_edited_group_added.tpl | 1 + .../messages_sent_edited_group_edited.tpl | 1 + .../sms_templates/new_answer_posted_forum.tpl | 1 + .../sms_templates/new_course_been_created.tpl | 1 + .../new_course_suggested_teacher.tpl | 1 + .../new_file_shared_course_by.tpl | 1 + .../new_system_announcement_added.tpl | 1 + .../new_thread_started_forum.tpl | 1 + .../new_user_pending_approval.tpl | 1 + .../new_user_subscribed_course.tpl | 1 + .../password_updated_login_password.tpl | 1 + .../sms_templates/posted_forum_course.tpl | 1 + .../received_new_personal_messages.tpl | 1 + .../reminder_assignment_course_due.tpl | 1 + .../request_open_course_code_rejected.tpl | 1 + .../requested_password_change.tpl | 1 + .../sms_templates/student_answered_test.tpl | 1 + .../student_answered_test_open_question.tpl | 1 + .../student_answered_test_voice_question.tpl | 1 + .../sms_templates/subscribed_session.tpl | 1 + .../sms_templates/subscribed_session_csv.tpl | 1 + .../system_announcement_update.tpl | 1 + .../test_new_system_announcement_added.tpl | 1 + .../test_system_announcement_update.tpl | 1 + .../user_answered_inbox_message.tpl | 1 + .../sms_templates/user_details_modified.tpl | 1 + .../user_suggested_be_friends.tpl | 1 + .../user_uploaded_assignment_check.tpl | 1 + ...assignment_check_student_submits_paper.tpl | 1 + .../user_uploaded_assignment_course.tpl | 1 + ...ssignment_course_student_submits_paper.tpl | 1 + .../sms_templates/welcome_login_password.tpl | 1 + plugin/kannelsms/uninstall.php | 17 + plugin/kannelsms/vendor/changelog.md | 22 + plugin/kannelsms/vendor/exception.php | 27 + plugin/kannelsms/vendor/kannelsms_api.php | 643 + plugin/kannelsms/vendor/license.txt | 14 + plugin/kannelsms/vendor/readme.md | 253 + plugin/keycloak/KeycloakPlugin.php | 62 + plugin/keycloak/README.md | 54 + plugin/keycloak/index.php | 27 + plugin/keycloak/lang/english.php | 6 + plugin/keycloak/metadata.php | 30 + plugin/keycloak/plugin.php | 4 + plugin/keycloak/settings.dist.php | 155 + plugin/keycloak/start.php | 220 + .../LearningCalendarPlugin.php | 1152 ++ plugin/learning_calendar/README.md | 2 + plugin/learning_calendar/ajax.php | 43 + plugin/learning_calendar/calendar.php | 50 + plugin/learning_calendar/calendar_users.php | 108 + plugin/learning_calendar/install.php | 8 + plugin/learning_calendar/lang/english.php | 22 + plugin/learning_calendar/lang/french.php | 22 + plugin/learning_calendar/lang/spanish.php | 22 + plugin/learning_calendar/plugin.php | 4 + plugin/learning_calendar/start.php | 191 + plugin/learning_calendar/uninstall.php | 4 + plugin/learning_calendar/view/calendar.tpl | 104 + plugin/learning_calendar/view/start.tpl | 3 + plugin/lti_provider/Entity/Platform.php | 234 + plugin/lti_provider/Entity/PlatformKey.php | 116 + plugin/lti_provider/Entity/Result.php | 267 + plugin/lti_provider/LtiProviderPlugin.php | 545 + plugin/lti_provider/README.md | 84 + plugin/lti_provider/admin.php | 34 + plugin/lti_provider/create.php | 59 + plugin/lti_provider/db/lti13_cache.php | 59 + plugin/lti_provider/db/lti13_cookie.php | 45 + plugin/lti_provider/db/lti13_database.php | 91 + plugin/lti_provider/delete.php | 32 + plugin/lti_provider/edit.php | 73 + plugin/lti_provider/install.php | 16 + plugin/lti_provider/lang/english.php | 34 + plugin/lti_provider/lang/french.php | 32 + plugin/lti_provider/lang/spanish.php | 34 + plugin/lti_provider/plugin.php | 6 + plugin/lti_provider/provider_settings.php | 61 + plugin/lti_provider/src/Form/FrmAdd.php | 88 + plugin/lti_provider/src/Form/FrmEdit.php | 94 + plugin/lti_provider/src/LtiProvider.php | 170 + plugin/lti_provider/tool/api/score.php | 61 + plugin/lti_provider/tool/jwks.php | 49 + plugin/lti_provider/tool/login.php | 7 + plugin/lti_provider/tool/logout.php | 10 + plugin/lti_provider/tool/start.php | 44 + plugin/lti_provider/uninstall.php | 16 + plugin/lti_provider/view/add.tpl | 19 + plugin/lti_provider/view/provider_admin.tpl | 52 + .../maintenancemode/MaintenanceModePlugin.php | 32 + plugin/maintenancemode/README.md | 9 + plugin/maintenancemode/index.php | 1 + plugin/maintenancemode/install.php | 1 + plugin/maintenancemode/lang/english.php | 13 + plugin/maintenancemode/lang/french.php | 13 + plugin/maintenancemode/lang/spanish.php | 13 + plugin/maintenancemode/plugin.php | 201 + plugin/migrationmoodle/README.md | 24 + plugin/migrationmoodle/admin.php | 241 + plugin/migrationmoodle/install.php | 59 + plugin/migrationmoodle/lang/english.php | 79 + plugin/migrationmoodle/lang/french.php | 24 + plugin/migrationmoodle/plugin.php | 4 + plugin/migrationmoodle/run_cli.php | 120 + .../src/Extractor/BaseExtractor.php | 63 + .../src/Extractor/FilterExtractor.php | 39 + .../LoadedCoursesFilterExtractor.php | 40 + .../Extractor/LoadedScormsFilterExtractor.php | 38 + .../Extractor/LoadedUsersFilterExtractor.php | 38 + .../src/Interfaces/ExtractorInterface.php | 22 + .../src/Interfaces/LoaderInterface.php | 17 + .../Interfaces/TransformPropertyInterface.php | 15 + .../src/Interfaces/TransformerInterface.php | 17 + .../src/Loader/CQuizLoader.php | 67 + .../src/Loader/CourseCategoriesLoader.php | 42 + .../src/Loader/CourseFilesLoader.php | 65 + .../src/Loader/CourseIntroductionLoader.php | 30 + .../src/Loader/CourseModulesLessonLoader.php | 40 + .../src/Loader/CourseModulesQuizLoader.php | 39 + .../src/Loader/CourseModulesScormLoader.php | 123 + .../src/Loader/CourseModulesUrlLoader.php | 35 + .../src/Loader/CourseSectionsLoader.php | 52 + .../src/Loader/CoursesLoader.php | 50 + .../src/Loader/FilesForScormScoLoader.php | 46 + .../src/Loader/LessonAnswersEssayLoader.php | 36 + .../Loader/LessonAnswersMatchingLoader.php | 68 + .../LessonAnswersMultipleAnswerLoader.php | 72 + .../LessonAnswersMultipleChoiceLoader.php | 22 + .../Loader/LessonAnswersShortAnswerLoader.php | 77 + .../Loader/LessonAnswersTrueFalseLoader.php | 70 + .../src/Loader/LessonPagesDocumentLoader.php | 47 + .../src/Loader/LessonPagesLoader.php | 40 + .../LessonQuestionPagesQuestionLoader.php | 40 + .../Loader/LessonQuestionPagesQuizLoader.php | 55 + .../src/Loader/QuestionCategoriesLoader.php | 44 + .../src/Loader/QuestionGapselectLoader.php | 37 + .../src/Loader/QuizzesLoader.php | 50 + .../src/Loader/QuizzesScoresLoader.php | 39 + .../src/Loader/RoleAssignmentsLoader.php | 31 + .../src/Loader/ScormScoLoader.php | 43 + .../src/Loader/SortSectionModuleLoader.php | 51 + .../src/Loader/TrackCourseAccessLoader.php | 66 + .../src/Loader/TrackLoginLoader.php | 57 + .../migrationmoodle/src/Loader/UrlLoader.php | 45 + .../src/Loader/UserLastLoginLoader.php | 28 + .../UserLearnPathLessonAttemptLoader.php | 32 + .../UserLearnPathLessonBranchLoader.php | 100 + .../Loader/UserLearnPathLessonTimerLoader.php | 90 + .../src/Loader/UserLearnPathQuizLoader.php | 66 + .../src/Loader/UserLearnPathsLoader.php | 73 + .../src/Loader/UserQuestionAttemptLoader.php | 30 + .../src/Loader/UserQuizAttemptLoader.php | 80 + .../src/Loader/UserSessionLoader.php | 79 + .../src/Loader/UsersLoader.php | 97 + .../src/Loader/UsersScormsViewLoader.php | 163 + .../src/Messages/ExtractMessage.php | 24 + .../src/Messages/LoadMessage.php | 42 + .../migrationmoodle/src/Messages/Message.php | 63 + .../src/Messages/TransformMessage.php | 40 + ...grationMoodleCheckLoginCredentialsHook.php | 82 + .../src/MigrationMoodlePlugin.php | 169 + .../migrationmoodle/src/Script/BaseScript.php | 94 + .../Script/UserLearnPathsProgressScript.php | 132 + .../src/Script/UserScormsProgressScript.php | 138 + plugin/migrationmoodle/src/Task/BaseTask.php | 199 + plugin/migrationmoodle/src/Task/CQuizTask.php | 77 + .../src/Task/CourseCategoriesTask.php | 61 + .../src/Task/CourseFilesTask.php | 47 + .../src/Task/CourseIntroductionsTask.php | 64 + .../src/Task/CourseModulesLessonTask.php | 68 + .../src/Task/CourseModulesQuizTask.php | 68 + .../src/Task/CourseModulesScormTask.php | 92 + .../src/Task/CourseModulesUrlTask.php | 75 + .../src/Task/CourseSectionsTask.php | 74 + .../migrationmoodle/src/Task/CoursesTask.php | 85 + .../Task/FilesForCourseIntroductionsTask.php | 41 + .../src/Task/FilesForCourseSectionsTask.php | 49 + .../src/Task/FilesForLessonAnswersTask.php | 41 + .../src/Task/FilesForLessonPagesTask.php | 41 + .../src/Task/FilesForQuizzesTask.php | 39 + .../src/Task/FilesForScormScoesTask.php | 83 + .../src/Task/LessonAnswersEssayTask.php | 70 + .../src/Task/LessonAnswersMatchingTask.php | 83 + .../Task/LessonAnswersMultipleAnswerTask.php | 43 + .../Task/LessonAnswersMultipleChoiceTask.php | 43 + .../src/Task/LessonAnswersShortAnswerTask.php | 83 + .../src/Task/LessonAnswersTrueFalseTask.php | 85 + .../src/Task/LessonPagesDocumentTask.php | 75 + .../src/Task/LessonPagesQuizQuestionTask.php | 77 + .../src/Task/LessonPagesQuizTask.php | 65 + .../src/Task/LessonPagesTask.php | 104 + .../src/Task/QuestionCategoriesTask.php | 62 + .../src/Task/QuestionGapselectTask.php | 90 + .../Task/QuestionMultiChoiceMultipleTask.php | 51 + .../Task/QuestionMultiChoiceSingleTask.php | 92 + .../src/Task/QuestionShortAnswerTask.php | 73 + .../src/Task/QuestionsTask.php | 76 + .../src/Task/QuestionsTrueFalseTask.php | 40 + .../src/Task/QuizzesScoresTask.php | 74 + .../migrationmoodle/src/Task/QuizzesTask.php | 97 + .../src/Task/RoleAssignmentsTask.php | 72 + .../src/Task/ScormScoesTask.php | 71 + .../src/Task/SortSectionModulesTask.php | 78 + .../src/Task/TrackCourseAccessTask.php | 86 + .../src/Task/TrackLoginTask.php | 71 + plugin/migrationmoodle/src/Task/UrlsTask.php | 79 + .../UserQuestionAttemptsGapselectTask.php | 22 + .../UserQuestionAttemptsShortanswerTask.php | 22 + .../src/Task/UserQuestionAttemptsTask.php | 124 + .../UserQuestionAttemptsTruefalseTask.php | 22 + .../src/Task/UserSessionsTask.php | 87 + .../src/Task/UsersLastLoginTask.php | 68 + .../UsersLearnPathsLessonAttemptsTask.php | 58 + .../Task/UsersLearnPathsLessonBranchTask.php | 101 + .../Task/UsersLearnPathsLessonTimerTask.php | 83 + .../src/Task/UsersLearnPathsQuizzesTask.php | 90 + .../src/Task/UsersLearnPathsTask.php | 64 + .../src/Task/UsersQuizzesAttemptsTask.php | 102 + .../src/Task/UsersScormsViewTask.php | 98 + plugin/migrationmoodle/src/Task/UsersTask.php | 91 + .../src/Traits/FileFinderTrait.php | 35 + .../migrationmoodle/src/Traits/MapTrait.php | 22 + .../src/Transformer/BaseTransformer.php | 55 + .../src/Transformer/Property/AuthLookup.php | 22 + .../src/Transformer/Property/Copy.php | 22 + .../Property/CourseCategoryLookup.php | 40 + .../src/Transformer/Property/CourseCode.php | 30 + .../Transformer/Property/CourseUserStatus.php | 27 + .../Transformer/Property/CourseVisibility.php | 29 + .../Property/CoursesArrayLookup.php | 33 + .../Transformer/Property/DateTimeObject.php | 32 + .../src/Transformer/Property/Explode.php | 26 + .../src/Transformer/Property/IsFalse.php | 22 + .../src/Transformer/Property/Language.php | 37 + .../Property/LearnPathItemViewQuizStatus.php | 58 + .../Property/LessonAnswersMatchingScore.php | 59 + .../Transformer/Property/LessonPageType.php | 36 + .../Property/LoadedCourseCodeLookup.php | 33 + ...LoadedCourseFromQuestionCategoryLookup.php | 79 + .../Property/LoadedCourseLookup.php | 22 + .../LoadedCourseModuleLessonLookup.php | 55 + .../LoadedCourseModuleQuizByQuizLookup.php | 60 + .../Property/LoadedCourseModuleQuizLookup.php | 22 + .../Property/LoadedCourseModuleUrlLookup.php | 22 + .../LoadedCourseSectionFromLessonLookup.php | 52 + .../Property/LoadedCourseSectionLookup.php | 22 + .../Transformer/Property/LoadedKeyLookup.php | 60 + .../Property/LoadedLessonPageLookup.php | 22 + .../Property/LoadedLessonPageQuizLookup.php | 24 + .../LoadedLessonPageQuizQuestionLookup.php | 24 + .../Property/LoadedQuestionLookup.php | 22 + .../Transformer/Property/LoadedQuizLookup.php | 22 + .../Property/LoadedScormLookup.php | 22 + .../Property/LoadedScormScoLookup.php | 22 + .../Transformer/Property/LoadedUserLookup.php | 21 + .../Property/LoadedUserQuizLookup.php | 22 + .../Property/LoadedUserSessionLookup.php | 22 + .../src/Transformer/Property/Percentage.php | 28 + .../Property/QuestionGapselectAnswer.php | 77 + .../src/Transformer/Property/QuestionType.php | 86 + .../Transformer/Property/QuizDataTracking.php | 68 + .../QuizQuestionTypeFromLessonPage.php | 52 + .../Transformer/Property/ReplaceFilePaths.php | 92 + .../Property/ScormScoParentLookup.php | 41 + .../Property/ScormScoTrackData.php | 130 + .../Property/SectionSequenceLookup.php | 71 + .../src/Transformer/Property/SessionName.php | 30 + .../src/Transformer/Property/Subtract.php | 26 + .../src/Transformer/Property/UserActive.php | 29 + .../Property/UserQuestionAnswer.php | 40 + .../Property/UserQuestionAnswerGapselect.php | 139 + .../UserQuestionAnswerShortanswer.php | 35 + .../Property/UserQuestionAnswerTruefalse.php | 57 + .../src/Transformer/Property/UserStatus.php | 62 + .../Property/WrapHtmlReplacingFilePaths.php | 36 + plugin/migrationmoodle/uninstall.php | 57 + plugin/mindmap/ajax/mindmap.ajax.php | 59 + plugin/mindmap/edit-mindmap/README.md | 13 + plugin/mindmap/edit-mindmap/app-base.js | 88 + plugin/mindmap/edit-mindmap/build/config.json | 8 + .../mindmap/edit-mindmap/build/lib/combine.js | 75 + .../mindmap/edit-mindmap/build/lib/config.js | 19 + .../edit-mindmap/build/lib/dependency.js | 136 + plugin/mindmap/edit-mindmap/build/make.js | 16 + plugin/mindmap/edit-mindmap/build/makeDeps.js | 7 + .../edit-mindmap/doc/code-structure.md | 31 + plugin/mindmap/edit-mindmap/doc/note.md | 31 + plugin/mindmap/edit-mindmap/img/mindmap.png | Bin 0 -> 3261 bytes .../mindmap/edit-mindmap/img/mindmap128.png | Bin 0 -> 8107 bytes .../edit-mindmap/img/mindmap128gray.png | Bin 0 -> 3551 bytes plugin/mindmap/edit-mindmap/index.php | 134 + plugin/mindmap/edit-mindmap/mindMap.js | 11 + plugin/mindmap/edit-mindmap/mindMap.txt | 52 + .../vendor/css/DemoTransformsAnimations.css | 216 + .../vendor/css/FullPageAnimationsPrefixed.css | 223 + .../mindmap/edit-mindmap/vendor/css/app.css | 174 + .../vendor/css/bootstrap-responsive.css | 1092 ++ .../vendor/css/bootstrap-responsive.min.css | 9 + .../edit-mindmap/vendor/css/bootstrap.css | 6039 ++++++ .../edit-mindmap/vendor/css/bootstrap.min.css | 9 + .../mindmap/edit-mindmap/vendor/css/help.css | 6 + .../mindmap/edit-mindmap/vendor/css/qunit.css | 236 + .../edit-mindmap/vendor/help/introduce.html | 68 + .../edit-mindmap/vendor/help/license.html | 46 + .../mindmap/edit-mindmap/vendor/help/map.html | 103 + .../edit-mindmap/vendor/help/node.html | 99 + .../edit-mindmap/vendor/html/index.html | 105 + .../edit-mindmap/vendor/html/index2.html | 225 + .../edit-mindmap/vendor/html/template.html | 9 + plugin/mindmap/edit-mindmap/vendor/img/bg.gif | Bin 0 -> 881 bytes .../vendor/img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../vendor/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes .../vendor/img/help/append_child_node.png | Bin 0 -> 9293 bytes .../vendor/img/help/copy_node.png | Bin 0 -> 10231 bytes .../vendor/img/help/create_new_map.png | Bin 0 -> 10850 bytes .../vendor/img/help/create_root_node.png | Bin 0 -> 7330 bytes .../edit-mindmap/vendor/img/help/cut_node.png | Bin 0 -> 9264 bytes .../edit-mindmap/vendor/img/help/delete.png | Bin 0 -> 9518 bytes .../vendor/img/help/edit_node.png | Bin 0 -> 9272 bytes .../edit-mindmap/vendor/img/help/help.png | Bin 0 -> 2780 bytes .../vendor/img/help/open_map_in_disk.png | Bin 0 -> 11361 bytes .../vendor/img/help/open_map_in_storage.png | Bin 0 -> 11206 bytes .../vendor/img/help/paste_node.png | Bin 0 -> 9462 bytes .../edit-mindmap/vendor/img/help/redo1.png | Bin 0 -> 16596 bytes .../edit-mindmap/vendor/img/help/redo2.png | Bin 0 -> 10514 bytes .../vendor/img/help/save_map_in_disk.png | Bin 0 -> 12104 bytes .../vendor/img/help/save_map_in_storage.png | Bin 0 -> 11931 bytes .../edit-mindmap/vendor/img/help/undo1.png | Bin 0 -> 17048 bytes .../edit-mindmap/vendor/img/help/undo2.png | Bin 0 -> 10502 bytes plugin/mindmap/edit-mindmap/vendor/index.html | 112 + .../edit-mindmap/vendor/js/MapManager.js | 255 + .../edit-mindmap/vendor/js/MapsManager.js | 125 + plugin/mindmap/edit-mindmap/vendor/js/base.js | 369 + .../mindmap/edit-mindmap/vendor/js/branch.js | 184 + .../mindmap/edit-mindmap/vendor/js/caption.js | 96 + .../mindmap/edit-mindmap/vendor/js/command.js | 431 + .../vendor/js/commandcontroller.js | 84 + .../edit-mindmap/vendor/js/commandstack.js | 124 + .../edit-mindmap/vendor/js/component.js | 165 + .../edit-mindmap/vendor/js/composition.js | 197 + plugin/mindmap/edit-mindmap/vendor/js/deps.js | 40 + plugin/mindmap/edit-mindmap/vendor/js/help.js | 144 + .../mindmap/edit-mindmap/vendor/js/jquery.js | 2 + .../edit-mindmap/vendor/js/kampfer/ajax.js | 132 + .../edit-mindmap/vendor/js/kampfer/base.js | 466 + .../edit-mindmap/vendor/js/kampfer/class.js | 31 + .../vendor/js/kampfer/class/class.js | 31 + .../vendor/js/kampfer/class/composition.js | 198 + .../vendor/js/kampfer/class/dialog.js | 94 + .../vendor/js/kampfer/class/eventtarget.js | 43 + .../vendor/js/kampfer/class/uicomponent.js | 150 + .../edit-mindmap/vendor/js/kampfer/data.js | 255 + .../edit-mindmap/vendor/js/kampfer/deps.js | 7 + .../edit-mindmap/vendor/js/kampfer/dom.js | 165 + .../edit-mindmap/vendor/js/kampfer/events.js | 468 + .../vendor/js/kampfer/eventtarget.js | 43 + .../vendor/js/kampfer/helper/es5-shim.js | 981 + .../vendor/js/kampfer/helper/json2.js | 488 + .../edit-mindmap/vendor/js/kampfer/support.js | 18 + plugin/mindmap/edit-mindmap/vendor/js/map.js | 92 + plugin/mindmap/edit-mindmap/vendor/js/menu.js | 98 + .../mindmap/edit-mindmap/vendor/js/mindmap.js | 48 + plugin/mindmap/edit-mindmap/vendor/js/node.js | 86 + .../edit-mindmap/vendor/js/openmapdialog.js | 128 + .../vendor/js/plugins/BlobBuilder.min.js | 6 + .../vendor/js/plugins/FileSaver.min.js | 6 + .../vendor/js/plugins/modernizr-latest.js | 1393 ++ .../vendor/js/plugins/mousetrap.min.js | 10 + .../vendor/js/plugins/store.min.js | 6 + .../mindmap/edit-mindmap/vendor/js/radio.js | 5 + .../edit-mindmap/vendor/js/renamemapdialog.js | 125 + .../mindmap/edit-mindmap/vendor/js/toolbar.js | 42 + .../mindmap/edit-mindmap/vendor/js/window.js | 104 + plugin/mindmap/edit-mindmap/vendor/map.json | 74 + plugin/mindmap/edit-mindmap/vendor/mindmap.mf | 9 + .../vendor/test/test_composition.html | 211 + .../vendor/test/test_domDepth.html | 91 + .../edit-mindmap/vendor/test/test_events.html | 394 + .../edit-mindmap/vendor/test/test_index.html | 67 + .../vendor/test/test_parentNode_null.html | 18 + .../vendor/test/test_template.html | 25 + plugin/mindmap/img/22/mindmap.png | Bin 0 -> 11916 bytes plugin/mindmap/img/22/mindmap_na.png | Bin 0 -> 9815 bytes plugin/mindmap/img/32/mindmap.png | Bin 0 -> 4984 bytes plugin/mindmap/img/32/mindmap_na.png | Bin 0 -> 3827 bytes plugin/mindmap/img/64/mindmap.png | Bin 0 -> 2151 bytes plugin/mindmap/img/64/mindmap_na.png | Bin 0 -> 2032 bytes plugin/mindmap/img/edit.png | Bin 0 -> 591 bytes plugin/mindmap/img/eyes.png | Bin 0 -> 767 bytes plugin/mindmap/img/mindmap.png | Bin 0 -> 3261 bytes plugin/mindmap/img/mindmap128.png | Bin 0 -> 8107 bytes plugin/mindmap/img/mindmap24.png | Bin 0 -> 1223 bytes plugin/mindmap/img/mindmap600.png | Bin 0 -> 3403 bytes plugin/mindmap/img/sumin.png | Bin 0 -> 672 bytes plugin/mindmap/inc/action.switch.php | 60 + plugin/mindmap/inc/edit.form.php | 25 + plugin/mindmap/index.php | 5 + plugin/mindmap/install.php | 10 + plugin/mindmap/lang/english.php | 16 + plugin/mindmap/lang/french.php | 16 + plugin/mindmap/lang/spanish.php | 16 + plugin/mindmap/list.php | 149 + plugin/mindmap/mindmap_plugin.class.php | 111 + plugin/mindmap/plugin.php | 6 + plugin/mindmap/resources/img/docx.png | Bin 0 -> 2767 bytes plugin/mindmap/resources/img/download.png | Bin 0 -> 755 bytes plugin/mindmap/resources/img/file-docx.png | Bin 0 -> 2414 bytes plugin/mindmap/resources/img/file-none.png | Bin 0 -> 699 bytes plugin/mindmap/resources/img/file-pdf.png | Bin 0 -> 1470 bytes .../mindmap/resources/img/forum_listview.png | Bin 0 -> 708 bytes plugin/mindmap/resources/img/g91884.png | Bin 0 -> 1470 bytes plugin/mindmap/resources/img/lock.png | Bin 0 -> 890 bytes plugin/mindmap/resources/img/pdf.png | Bin 0 -> 1355 bytes .../img/reading-comprehension_na.png | Bin 0 -> 1104 bytes plugin/mindmap/resources/img/statistics.png | Bin 0 -> 3758 bytes plugin/mindmap/resources/img/stats.png | Bin 0 -> 2450 bytes .../img/survey_reporting_overall.png | Bin 0 -> 1684 bytes .../resources/js/jquery.dataTables.min.js | 164 + plugin/mindmap/start.php | 3 + plugin/mindmap/uninstall.php | 9 + plugin/mindmap/view/list.tpl | 54 + plugin/nosearchindex/NoSearchIndex.php | 34 + plugin/nosearchindex/README.md | 4 + plugin/nosearchindex/index.php | 1 + plugin/nosearchindex/install.php | 1 + plugin/nosearchindex/lang/english.php | 8 + plugin/nosearchindex/plugin.php | 91 + plugin/notebookteacher/README.md | 20 + plugin/notebookteacher/config.php | 10 + plugin/notebookteacher/database.php | 46 + plugin/notebookteacher/index.php | 3 + plugin/notebookteacher/install.php | 14 + plugin/notebookteacher/lang/english.php | 14 + plugin/notebookteacher/lang/spanish.php | 14 + plugin/notebookteacher/plugin.php | 16 + .../resources/img/22/notebookteacher.png | Bin 0 -> 1190 bytes .../resources/img/32/notebookteacher.png | Bin 0 -> 2503 bytes .../resources/img/32/notebookteacher_na.png | Bin 0 -> 3703 bytes .../resources/img/64/notebookteacher.png | Bin 0 -> 4827 bytes .../resources/img/64/notebookteacher_na.png | Bin 0 -> 3383 bytes .../notebookteacher/src/NotebookTeacher.php | 533 + .../src/NotebookTeacherPlugin.php | 130 + plugin/notebookteacher/src/index.php | 225 + plugin/notebookteacher/start.php | 33 + plugin/notebookteacher/uninstall.php | 12 + plugin/notebookteacher/update.php | 14 + plugin/oauth2/README.md | 22 + plugin/oauth2/config.dist.php | 14 + plugin/oauth2/index.php | 51 + plugin/oauth2/install.php | 8 + plugin/oauth2/lang/english.php | 144 + plugin/oauth2/layout/login_form.tpl | 44 + plugin/oauth2/localLogin/images/footer.png | Bin 0 -> 2610 bytes plugin/oauth2/localLogin/images/header.png | Bin 0 -> 2627 bytes plugin/oauth2/localLogin/login.php | 104 + plugin/oauth2/login.php | 35 + plugin/oauth2/plugin.php | 11 + plugin/oauth2/redirect_info.php | 37 + plugin/oauth2/src/OAuth2.php | 563 + plugin/oauth2/src/callback.php | 100 + plugin/oauth2/src/oidc_login.php | 54 + plugin/oauth2/view/block.tpl | 18 + plugin/oauth2/view/login.tpl | 19 + plugin/olpc_peru_filter/README.md | 72 + plugin/olpc_peru_filter/config.php | 13 + plugin/olpc_peru_filter/index.php | 4 + plugin/olpc_peru_filter/install.php | 13 + plugin/olpc_peru_filter/lang/english.php | 8 + plugin/olpc_peru_filter/lang/spanish.php | 8 + .../lib/olpc_peru_filter_plugin.class.php | 148 + plugin/olpc_peru_filter/plugin.php | 4 + plugin/olpc_peru_filter/uninstall.php | 14 + plugin/onlyoffice/.php-cs-fixer.dist.php | 37 + plugin/onlyoffice/.php-cs-fixer.php | 3 + plugin/onlyoffice/AUTHORS.md | 4 + plugin/onlyoffice/CHANGELOG.md | 52 + plugin/onlyoffice/LICENSE.txt | 201 + plugin/onlyoffice/README.md | 216 + plugin/onlyoffice/ajax/saveas.php | 85 + plugin/onlyoffice/assets/AUTHORS.md | 4 + plugin/onlyoffice/assets/LICENSE.txt | 201 + plugin/onlyoffice/assets/README.md | 6 + plugin/onlyoffice/assets/ar/docx.zip | Bin 0 -> 7693 bytes plugin/onlyoffice/assets/ar/pdf.zip | Bin 0 -> 10770 bytes plugin/onlyoffice/assets/ar/pptx.zip | Bin 0 -> 27820 bytes plugin/onlyoffice/assets/ar/xlsx.zip | Bin 0 -> 6384 bytes plugin/onlyoffice/assets/az/docx.zip | Bin 0 -> 6950 bytes plugin/onlyoffice/assets/az/pdf.zip | Bin 0 -> 10437 bytes plugin/onlyoffice/assets/az/pptx.zip | Bin 0 -> 35268 bytes plugin/onlyoffice/assets/az/xlsx.zip | Bin 0 -> 6310 bytes plugin/onlyoffice/assets/bg/docx.zip | Bin 0 -> 6688 bytes plugin/onlyoffice/assets/bg/pdf.zip | Bin 0 -> 10283 bytes plugin/onlyoffice/assets/bg/pptx.zip | Bin 0 -> 30614 bytes plugin/onlyoffice/assets/bg/xlsx.zip | Bin 0 -> 6337 bytes plugin/onlyoffice/assets/cs/docx.zip | Bin 0 -> 6946 bytes plugin/onlyoffice/assets/cs/pdf.zip | Bin 0 -> 10433 bytes plugin/onlyoffice/assets/cs/pptx.zip | Bin 0 -> 33098 bytes plugin/onlyoffice/assets/cs/xlsx.zip | Bin 0 -> 5660 bytes plugin/onlyoffice/assets/de/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/de/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/de/pptx.zip | Bin 0 -> 32875 bytes plugin/onlyoffice/assets/de/xlsx.zip | Bin 0 -> 6345 bytes plugin/onlyoffice/assets/default/docx.zip | Bin 0 -> 6940 bytes plugin/onlyoffice/assets/default/pdf.zip | Bin 0 -> 10427 bytes plugin/onlyoffice/assets/default/pptx.zip | Bin 0 -> 34699 bytes plugin/onlyoffice/assets/default/xlsx.zip | Bin 0 -> 6352 bytes plugin/onlyoffice/assets/el/docx.zip | Bin 0 -> 6943 bytes plugin/onlyoffice/assets/el/pdf.zip | Bin 0 -> 10430 bytes plugin/onlyoffice/assets/el/pptx.zip | Bin 0 -> 34290 bytes plugin/onlyoffice/assets/el/xlsx.zip | Bin 0 -> 6319 bytes plugin/onlyoffice/assets/en-GB/docx.zip | Bin 0 -> 6942 bytes plugin/onlyoffice/assets/en-GB/pdf.zip | Bin 0 -> 10429 bytes plugin/onlyoffice/assets/en-GB/pptx.zip | Bin 0 -> 34696 bytes plugin/onlyoffice/assets/en-GB/xlsx.zip | Bin 0 -> 6345 bytes plugin/onlyoffice/assets/en/docx.zip | Bin 0 -> 7726 bytes plugin/onlyoffice/assets/en/pdf.zip | Bin 0 -> 10266 bytes plugin/onlyoffice/assets/en/pptx.zip | Bin 0 -> 34699 bytes plugin/onlyoffice/assets/en/xlsx.zip | Bin 0 -> 5731 bytes plugin/onlyoffice/assets/es/docx.zip | Bin 0 -> 6943 bytes plugin/onlyoffice/assets/es/pdf.zip | Bin 0 -> 10430 bytes plugin/onlyoffice/assets/es/pptx.zip | Bin 0 -> 33000 bytes plugin/onlyoffice/assets/es/xlsx.zip | Bin 0 -> 6303 bytes plugin/onlyoffice/assets/eu/docx.zip | Bin 0 -> 7748 bytes plugin/onlyoffice/assets/eu/pdf.zip | Bin 0 -> 10430 bytes plugin/onlyoffice/assets/eu/pptx.zip | Bin 0 -> 34917 bytes plugin/onlyoffice/assets/eu/xlsx.zip | Bin 0 -> 6341 bytes plugin/onlyoffice/assets/fi/docx.zip | Bin 0 -> 7699 bytes plugin/onlyoffice/assets/fi/pdf.zip | Bin 0 -> 10320 bytes plugin/onlyoffice/assets/fi/pptx.zip | Bin 0 -> 27407 bytes plugin/onlyoffice/assets/fi/xlsx.zip | Bin 0 -> 6372 bytes plugin/onlyoffice/assets/fr/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/fr/pdf.zip | Bin 0 -> 10429 bytes plugin/onlyoffice/assets/fr/pptx.zip | Bin 0 -> 33058 bytes plugin/onlyoffice/assets/fr/xlsx.zip | Bin 0 -> 6304 bytes plugin/onlyoffice/assets/gl/docx.zip | Bin 0 -> 7925 bytes plugin/onlyoffice/assets/gl/pdf.zip | Bin 0 -> 10430 bytes plugin/onlyoffice/assets/gl/pptx.zip | Bin 0 -> 33034 bytes plugin/onlyoffice/assets/gl/xlsx.zip | Bin 0 -> 6340 bytes plugin/onlyoffice/assets/he/docx.zip | Bin 0 -> 6957 bytes plugin/onlyoffice/assets/he/pdf.zip | Bin 0 -> 10297 bytes plugin/onlyoffice/assets/he/pptx.zip | Bin 0 -> 32973 bytes plugin/onlyoffice/assets/he/xlsx.zip | Bin 0 -> 5677 bytes plugin/onlyoffice/assets/hy/docx.zip | Bin 0 -> 7763 bytes plugin/onlyoffice/assets/hy/pdf.zip | Bin 0 -> 10444 bytes plugin/onlyoffice/assets/hy/pptx.zip | Bin 0 -> 35958 bytes plugin/onlyoffice/assets/hy/xlsx.zip | Bin 0 -> 6354 bytes plugin/onlyoffice/assets/it/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/it/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/it/pptx.zip | Bin 0 -> 32950 bytes plugin/onlyoffice/assets/it/xlsx.zip | Bin 0 -> 6303 bytes plugin/onlyoffice/assets/ja/docx.zip | Bin 0 -> 8375 bytes plugin/onlyoffice/assets/ja/pdf.zip | Bin 0 -> 11251 bytes plugin/onlyoffice/assets/ja/pptx.zip | Bin 0 -> 33025 bytes plugin/onlyoffice/assets/ja/xlsx.zip | Bin 0 -> 7840 bytes plugin/onlyoffice/assets/ko/docx.zip | Bin 0 -> 6937 bytes plugin/onlyoffice/assets/ko/pdf.zip | Bin 0 -> 10424 bytes plugin/onlyoffice/assets/ko/pptx.zip | Bin 0 -> 33819 bytes plugin/onlyoffice/assets/ko/xlsx.zip | Bin 0 -> 6314 bytes plugin/onlyoffice/assets/lv/docx.zip | Bin 0 -> 6945 bytes plugin/onlyoffice/assets/lv/pdf.zip | Bin 0 -> 10432 bytes plugin/onlyoffice/assets/lv/pptx.zip | Bin 0 -> 32892 bytes plugin/onlyoffice/assets/lv/xlsx.zip | Bin 0 -> 6302 bytes plugin/onlyoffice/assets/ms/docx.zip | Bin 0 -> 6946 bytes plugin/onlyoffice/assets/ms/pdf.zip | Bin 0 -> 10433 bytes plugin/onlyoffice/assets/ms/pptx.zip | Bin 0 -> 34744 bytes plugin/onlyoffice/assets/ms/xlsx.zip | Bin 0 -> 6341 bytes plugin/onlyoffice/assets/nl/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/nl/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/nl/pptx.zip | Bin 0 -> 32635 bytes plugin/onlyoffice/assets/nl/xlsx.zip | Bin 0 -> 6339 bytes plugin/onlyoffice/assets/no/docx.zip | Bin 0 -> 7698 bytes plugin/onlyoffice/assets/no/pdf.zip | Bin 0 -> 10320 bytes plugin/onlyoffice/assets/no/pptx.zip | Bin 0 -> 27115 bytes plugin/onlyoffice/assets/no/xlsx.zip | Bin 0 -> 5640 bytes plugin/onlyoffice/assets/pl/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/pl/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/pl/pptx.zip | Bin 0 -> 33016 bytes plugin/onlyoffice/assets/pl/xlsx.zip | Bin 0 -> 6303 bytes plugin/onlyoffice/assets/pt-BR/docx.zip | Bin 0 -> 8504 bytes plugin/onlyoffice/assets/pt-BR/pdf.zip | Bin 0 -> 11380 bytes plugin/onlyoffice/assets/pt-BR/pptx.zip | Bin 0 -> 33062 bytes plugin/onlyoffice/assets/pt-BR/xlsx.zip | Bin 0 -> 6477 bytes plugin/onlyoffice/assets/pt/docx.zip | Bin 0 -> 8504 bytes plugin/onlyoffice/assets/pt/pdf.zip | Bin 0 -> 11380 bytes plugin/onlyoffice/assets/pt/pptx.zip | Bin 0 -> 32980 bytes plugin/onlyoffice/assets/pt/xlsx.zip | Bin 0 -> 6517 bytes plugin/onlyoffice/assets/ru/docx.zip | Bin 0 -> 6944 bytes plugin/onlyoffice/assets/ru/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/ru/pptx.zip | Bin 0 -> 33670 bytes plugin/onlyoffice/assets/ru/xlsx.zip | Bin 0 -> 6360 bytes plugin/onlyoffice/assets/si/docx.zip | Bin 0 -> 8497 bytes plugin/onlyoffice/assets/si/pdf.zip | Bin 0 -> 11373 bytes plugin/onlyoffice/assets/si/pptx.zip | Bin 0 -> 34458 bytes plugin/onlyoffice/assets/si/xlsx.zip | Bin 0 -> 6548 bytes plugin/onlyoffice/assets/sk/docx.zip | Bin 0 -> 8373 bytes plugin/onlyoffice/assets/sk/pdf.zip | Bin 0 -> 11249 bytes plugin/onlyoffice/assets/sk/pptx.zip | Bin 0 -> 34947 bytes plugin/onlyoffice/assets/sk/xlsx.zip | Bin 0 -> 6302 bytes plugin/onlyoffice/assets/sl/docx.zip | Bin 0 -> 7698 bytes plugin/onlyoffice/assets/sl/pdf.zip | Bin 0 -> 10320 bytes plugin/onlyoffice/assets/sl/pptx.zip | Bin 0 -> 27177 bytes plugin/onlyoffice/assets/sl/xlsx.zip | Bin 0 -> 6371 bytes plugin/onlyoffice/assets/sr-Cyrl-RS/docx.zip | Bin 0 -> 7954 bytes plugin/onlyoffice/assets/sr-Cyrl-RS/pdf.zip | Bin 0 -> 10324 bytes plugin/onlyoffice/assets/sr-Cyrl-RS/pptx.zip | Bin 0 -> 30027 bytes plugin/onlyoffice/assets/sr-Cyrl-RS/xlsx.zip | Bin 0 -> 6424 bytes plugin/onlyoffice/assets/sr/docx.zip | Bin 0 -> 7702 bytes plugin/onlyoffice/assets/sr/pdf.zip | Bin 0 -> 10782 bytes plugin/onlyoffice/assets/sr/pptx.zip | Bin 0 -> 28767 bytes plugin/onlyoffice/assets/sr/xlsx.zip | Bin 0 -> 6370 bytes plugin/onlyoffice/assets/sv/docx.zip | Bin 0 -> 7899 bytes plugin/onlyoffice/assets/sv/pdf.zip | Bin 0 -> 10775 bytes plugin/onlyoffice/assets/sv/pptx.zip | Bin 0 -> 29047 bytes plugin/onlyoffice/assets/sv/xlsx.zip | Bin 0 -> 6370 bytes plugin/onlyoffice/assets/tr/docx.zip | Bin 0 -> 6956 bytes plugin/onlyoffice/assets/tr/pdf.zip | Bin 0 -> 10443 bytes plugin/onlyoffice/assets/tr/pptx.zip | Bin 0 -> 33275 bytes plugin/onlyoffice/assets/tr/xlsx.zip | Bin 0 -> 6341 bytes plugin/onlyoffice/assets/uk/docx.zip | Bin 0 -> 6945 bytes plugin/onlyoffice/assets/uk/pdf.zip | Bin 0 -> 10431 bytes plugin/onlyoffice/assets/uk/pptx.zip | Bin 0 -> 34725 bytes plugin/onlyoffice/assets/uk/xlsx.zip | Bin 0 -> 6320 bytes plugin/onlyoffice/assets/vi/docx.zip | Bin 0 -> 7839 bytes plugin/onlyoffice/assets/vi/pdf.zip | Bin 0 -> 10526 bytes plugin/onlyoffice/assets/vi/pptx.zip | Bin 0 -> 33595 bytes plugin/onlyoffice/assets/vi/xlsx.zip | Bin 0 -> 6311 bytes plugin/onlyoffice/assets/zh-TW/docx.zip | Bin 0 -> 7839 bytes plugin/onlyoffice/assets/zh-TW/pdf.zip | Bin 0 -> 10526 bytes plugin/onlyoffice/assets/zh-TW/pptx.zip | Bin 0 -> 35545 bytes plugin/onlyoffice/assets/zh-TW/xlsx.zip | Bin 0 -> 6352 bytes plugin/onlyoffice/assets/zh/docx.zip | Bin 0 -> 7839 bytes plugin/onlyoffice/assets/zh/pdf.zip | Bin 0 -> 10526 bytes plugin/onlyoffice/assets/zh/pptx.zip | Bin 0 -> 34707 bytes plugin/onlyoffice/assets/zh/xlsx.zip | Bin 0 -> 6322 bytes plugin/onlyoffice/callback.php | 293 + plugin/onlyoffice/create.php | 110 + plugin/onlyoffice/editor.php | 284 + plugin/onlyoffice/error.php | 42 + plugin/onlyoffice/install.php | 22 + plugin/onlyoffice/lang/bulgarian.php | 43 + plugin/onlyoffice/lang/dutch.php | 43 + plugin/onlyoffice/lang/english.php | 43 + plugin/onlyoffice/lang/french.php | 43 + plugin/onlyoffice/lang/german.php | 43 + plugin/onlyoffice/lang/greek.php | 43 + plugin/onlyoffice/lang/italian.php | 43 + plugin/onlyoffice/lang/polish.php | 43 + plugin/onlyoffice/lang/portuguese.php | 43 + plugin/onlyoffice/lang/russian.php | 43 + plugin/onlyoffice/lang/spanish.php | 43 + .../layout/get_docs_cloud_banner.tpl | 19 + plugin/onlyoffice/lib/langManager.php | 48 + .../lib/onlyofficeActionObserver.php | 45 + .../onlyoffice/lib/onlyofficeAppRequests.php | 46 + .../onlyoffice/lib/onlyofficeAppSettings.php | 177 + .../lib/onlyofficeCallbackService.php | 150 + .../lib/onlyofficeConfigService.php | 179 + .../lib/onlyofficeDocumentManager.php | 284 + .../lib/onlyofficeFormatsManager.php | 73 + .../onlyoffice/lib/onlyofficeHttpClient.php | 66 + .../lib/onlyofficeItemActionObserver.php | 45 + .../lib/onlyofficeItemViewObserver.php | 41 + .../onlyoffice/lib/onlyofficeJwtManager.php | 44 + plugin/onlyoffice/lib/onlyofficePlugin.php | 143 + .../lib/onlyofficeSettingsFormBuilder.php | 150 + plugin/onlyoffice/lib/onlyofficeTools.php | 233 + plugin/onlyoffice/lib/templateManager.php | 36 + plugin/onlyoffice/plugin.php | 30 + .../resources/css/docsCloudBanner.css | 94 + .../resources/get-editors-background.svg | 5 + .../onlyoffice/resources/get-editors-pic.svg | 57 + .../resources/onlyoffice_create.png | Bin 0 -> 1157 bytes .../onlyoffice/resources/onlyoffice_edit.png | Bin 0 -> 1014 bytes .../onlyoffice/resources/onlyoffice_view.png | Bin 0 -> 673 bytes plugin/onlyoffice/uninstall.php | 22 + plugin/onlyoffice/vendor/autoload.php | 25 + .../vendor/composer/ClassLoader.php | 579 + .../vendor/composer/InstalledVersions.php | 359 + plugin/onlyoffice/vendor/composer/LICENSE | 21 + .../vendor/composer/autoload_classmap.php | 15 + .../vendor/composer/autoload_files.php | 12 + .../vendor/composer/autoload_namespaces.php | 9 + .../vendor/composer/autoload_psr4.php | 24 + .../vendor/composer/autoload_real.php | 50 + .../vendor/composer/autoload_static.php | 129 + .../onlyoffice/vendor/composer/installed.json | 533 + .../onlyoffice/vendor/composer/installed.php | 86 + .../vendor/composer/platform_check.php | 26 + .../graham-campbell/result-type/LICENSE | 21 + .../graham-campbell/result-type/composer.json | 33 + .../graham-campbell/result-type/src/Error.php | 121 + .../result-type/src/Result.php | 69 + .../result-type/src/Success.php | 120 + .../docs-integration-sdk/.env.example | 35 + .../docs-integration-sdk/.gitignore | 3 + .../docs-integration-sdk/AUTHORS.md | 4 + .../docs-integration-sdk/CHANGELOG.md | 26 + .../onlyoffice/docs-integration-sdk/LICENSE | 201 + .../onlyoffice/docs-integration-sdk/README.md | 162 + .../docs-integration-sdk/composer.json | 40 + .../onlyoffice/docs-integration-sdk/phpcs.xml | 18 + .../assets/document-formats/AUTHORS.md | 4 + .../assets/document-formats/CHANGELOG.md | 15 + .../resources/assets/document-formats/LICENSE | 201 + .../assets/document-formats/README.md | 52 + .../onlyoffice-docs-formats.json | 457 + .../assets/document-templates/AUTHORS.md | 4 + .../assets/document-templates/LICENSE | 201 + .../assets/document-templates/README.md | 15 + .../assets/document-templates/ar-SA/new.docx | Bin 0 -> 7693 bytes .../assets/document-templates/ar-SA/new.pdf | Bin 0 -> 10770 bytes .../assets/document-templates/ar-SA/new.pptx | Bin 0 -> 27820 bytes .../assets/document-templates/ar-SA/new.xlsx | Bin 0 -> 6384 bytes .../document-templates/az-Latn-AZ/new.docx | Bin 0 -> 6950 bytes .../document-templates/az-Latn-AZ/new.pdf | Bin 0 -> 10437 bytes .../document-templates/az-Latn-AZ/new.pptx | Bin 0 -> 35268 bytes .../document-templates/az-Latn-AZ/new.xlsx | Bin 0 -> 6310 bytes .../assets/document-templates/bg-BG/new.docx | Bin 0 -> 6688 bytes .../assets/document-templates/bg-BG/new.pdf | Bin 0 -> 10283 bytes .../assets/document-templates/bg-BG/new.pptx | Bin 0 -> 30614 bytes .../assets/document-templates/bg-BG/new.xlsx | Bin 0 -> 6337 bytes .../assets/document-templates/cs-CZ/new.docx | Bin 0 -> 6946 bytes .../assets/document-templates/cs-CZ/new.pdf | Bin 0 -> 10433 bytes .../assets/document-templates/cs-CZ/new.pptx | Bin 0 -> 33098 bytes .../assets/document-templates/cs-CZ/new.xlsx | Bin 0 -> 5660 bytes .../assets/document-templates/de-DE/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/de-DE/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/de-DE/new.pptx | Bin 0 -> 32875 bytes .../assets/document-templates/de-DE/new.xlsx | Bin 0 -> 6345 bytes .../document-templates/default/new.docx | Bin 0 -> 6940 bytes .../assets/document-templates/default/new.pdf | Bin 0 -> 10427 bytes .../document-templates/default/new.pptx | Bin 0 -> 34699 bytes .../document-templates/default/new.xlsx | Bin 0 -> 6352 bytes .../assets/document-templates/el-GR/new.docx | Bin 0 -> 6943 bytes .../assets/document-templates/el-GR/new.pdf | Bin 0 -> 10430 bytes .../assets/document-templates/el-GR/new.pptx | Bin 0 -> 34290 bytes .../assets/document-templates/el-GR/new.xlsx | Bin 0 -> 6319 bytes .../assets/document-templates/en-GB/new.docx | Bin 0 -> 6942 bytes .../assets/document-templates/en-GB/new.pdf | Bin 0 -> 10429 bytes .../assets/document-templates/en-GB/new.pptx | Bin 0 -> 34696 bytes .../assets/document-templates/en-GB/new.xlsx | Bin 0 -> 6345 bytes .../assets/document-templates/en-US/new.docx | Bin 0 -> 7726 bytes .../assets/document-templates/en-US/new.pdf | Bin 0 -> 10266 bytes .../assets/document-templates/en-US/new.pptx | Bin 0 -> 34699 bytes .../assets/document-templates/en-US/new.xlsx | Bin 0 -> 5731 bytes .../assets/document-templates/es-ES/new.docx | Bin 0 -> 6943 bytes .../assets/document-templates/es-ES/new.pdf | Bin 0 -> 10430 bytes .../assets/document-templates/es-ES/new.pptx | Bin 0 -> 33000 bytes .../assets/document-templates/es-ES/new.xlsx | Bin 0 -> 6303 bytes .../assets/document-templates/eu-ES/new.docx | Bin 0 -> 7748 bytes .../assets/document-templates/eu-ES/new.pdf | Bin 0 -> 10430 bytes .../assets/document-templates/eu-ES/new.pptx | Bin 0 -> 34917 bytes .../assets/document-templates/eu-ES/new.xlsx | Bin 0 -> 6341 bytes .../assets/document-templates/fi-FI/new.docx | Bin 0 -> 7699 bytes .../assets/document-templates/fi-FI/new.pdf | Bin 0 -> 10320 bytes .../assets/document-templates/fi-FI/new.pptx | Bin 0 -> 27407 bytes .../assets/document-templates/fi-FI/new.xlsx | Bin 0 -> 6372 bytes .../assets/document-templates/fr-FR/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/fr-FR/new.pdf | Bin 0 -> 10429 bytes .../assets/document-templates/fr-FR/new.pptx | Bin 0 -> 33058 bytes .../assets/document-templates/fr-FR/new.xlsx | Bin 0 -> 6304 bytes .../assets/document-templates/gl-ES/new.docx | Bin 0 -> 7925 bytes .../assets/document-templates/gl-ES/new.pdf | Bin 0 -> 10430 bytes .../assets/document-templates/gl-ES/new.pptx | Bin 0 -> 33034 bytes .../assets/document-templates/gl-ES/new.xlsx | Bin 0 -> 6340 bytes .../assets/document-templates/he-IL/new.docx | Bin 0 -> 6957 bytes .../assets/document-templates/he-IL/new.pdf | Bin 0 -> 10297 bytes .../assets/document-templates/he-IL/new.pptx | Bin 0 -> 32973 bytes .../assets/document-templates/he-IL/new.xlsx | Bin 0 -> 5677 bytes .../assets/document-templates/hy-AM/new.docx | Bin 0 -> 7763 bytes .../assets/document-templates/hy-AM/new.pdf | Bin 0 -> 10444 bytes .../assets/document-templates/hy-AM/new.pptx | Bin 0 -> 35958 bytes .../assets/document-templates/hy-AM/new.xlsx | Bin 0 -> 6354 bytes .../assets/document-templates/it-IT/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/it-IT/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/it-IT/new.pptx | Bin 0 -> 32950 bytes .../assets/document-templates/it-IT/new.xlsx | Bin 0 -> 6303 bytes .../assets/document-templates/ja-JP/new.docx | Bin 0 -> 8375 bytes .../assets/document-templates/ja-JP/new.pdf | Bin 0 -> 11251 bytes .../assets/document-templates/ja-JP/new.pptx | Bin 0 -> 33025 bytes .../assets/document-templates/ja-JP/new.xlsx | Bin 0 -> 7840 bytes .../assets/document-templates/ko-KR/new.docx | Bin 0 -> 6937 bytes .../assets/document-templates/ko-KR/new.pdf | Bin 0 -> 10424 bytes .../assets/document-templates/ko-KR/new.pptx | Bin 0 -> 33819 bytes .../assets/document-templates/ko-KR/new.xlsx | Bin 0 -> 6314 bytes .../assets/document-templates/lv-LV/new.docx | Bin 0 -> 6945 bytes .../assets/document-templates/lv-LV/new.pdf | Bin 0 -> 10432 bytes .../assets/document-templates/lv-LV/new.pptx | Bin 0 -> 32892 bytes .../assets/document-templates/lv-LV/new.xlsx | Bin 0 -> 6302 bytes .../assets/document-templates/ms-MY/new.docx | Bin 0 -> 6946 bytes .../assets/document-templates/ms-MY/new.pdf | Bin 0 -> 10433 bytes .../assets/document-templates/ms-MY/new.pptx | Bin 0 -> 34744 bytes .../assets/document-templates/ms-MY/new.xlsx | Bin 0 -> 6341 bytes .../assets/document-templates/nb-NO/new.docx | Bin 0 -> 7698 bytes .../assets/document-templates/nb-NO/new.pdf | Bin 0 -> 10320 bytes .../assets/document-templates/nb-NO/new.pptx | Bin 0 -> 27115 bytes .../assets/document-templates/nb-NO/new.xlsx | Bin 0 -> 5640 bytes .../assets/document-templates/nl-NL/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/nl-NL/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/nl-NL/new.pptx | Bin 0 -> 32635 bytes .../assets/document-templates/nl-NL/new.xlsx | Bin 0 -> 6339 bytes .../assets/document-templates/pl-PL/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/pl-PL/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/pl-PL/new.pptx | Bin 0 -> 33016 bytes .../assets/document-templates/pl-PL/new.xlsx | Bin 0 -> 6303 bytes .../assets/document-templates/pt-BR/new.docx | Bin 0 -> 8504 bytes .../assets/document-templates/pt-BR/new.pdf | Bin 0 -> 11380 bytes .../assets/document-templates/pt-BR/new.pptx | Bin 0 -> 33062 bytes .../assets/document-templates/pt-BR/new.xlsx | Bin 0 -> 6477 bytes .../assets/document-templates/pt-PT/new.docx | Bin 0 -> 8504 bytes .../assets/document-templates/pt-PT/new.pdf | Bin 0 -> 11380 bytes .../assets/document-templates/pt-PT/new.pptx | Bin 0 -> 32980 bytes .../assets/document-templates/pt-PT/new.xlsx | Bin 0 -> 6517 bytes .../assets/document-templates/ru-RU/new.docx | Bin 0 -> 6944 bytes .../assets/document-templates/ru-RU/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/ru-RU/new.pptx | Bin 0 -> 33670 bytes .../assets/document-templates/ru-RU/new.xlsx | Bin 0 -> 6360 bytes .../assets/document-templates/si-LK/new.docx | Bin 0 -> 8497 bytes .../assets/document-templates/si-LK/new.pdf | Bin 0 -> 11373 bytes .../assets/document-templates/si-LK/new.pptx | Bin 0 -> 34458 bytes .../assets/document-templates/si-LK/new.xlsx | Bin 0 -> 6548 bytes .../assets/document-templates/sk-SK/new.docx | Bin 0 -> 8373 bytes .../assets/document-templates/sk-SK/new.pdf | Bin 0 -> 11249 bytes .../assets/document-templates/sk-SK/new.pptx | Bin 0 -> 34947 bytes .../assets/document-templates/sk-SK/new.xlsx | Bin 0 -> 6302 bytes .../assets/document-templates/sl-SI/new.docx | Bin 0 -> 7698 bytes .../assets/document-templates/sl-SI/new.pdf | Bin 0 -> 10320 bytes .../assets/document-templates/sl-SI/new.pptx | Bin 0 -> 27177 bytes .../assets/document-templates/sl-SI/new.xlsx | Bin 0 -> 6371 bytes .../document-templates/sr-Cyrl-RS/new.docx | Bin 0 -> 7954 bytes .../document-templates/sr-Cyrl-RS/new.pdf | Bin 0 -> 10324 bytes .../document-templates/sr-Cyrl-RS/new.pptx | Bin 0 -> 30027 bytes .../document-templates/sr-Cyrl-RS/new.xlsx | Bin 0 -> 6424 bytes .../document-templates/sr-Latn-RS/new.docx | Bin 0 -> 7702 bytes .../document-templates/sr-Latn-RS/new.pdf | Bin 0 -> 10782 bytes .../document-templates/sr-Latn-RS/new.pptx | Bin 0 -> 28767 bytes .../document-templates/sr-Latn-RS/new.xlsx | Bin 0 -> 6370 bytes .../assets/document-templates/sv-SE/new.docx | Bin 0 -> 7899 bytes .../assets/document-templates/sv-SE/new.pdf | Bin 0 -> 10775 bytes .../assets/document-templates/sv-SE/new.pptx | Bin 0 -> 29047 bytes .../assets/document-templates/sv-SE/new.xlsx | Bin 0 -> 6370 bytes .../assets/document-templates/tr-TR/new.docx | Bin 0 -> 6956 bytes .../assets/document-templates/tr-TR/new.pdf | Bin 0 -> 10443 bytes .../assets/document-templates/tr-TR/new.pptx | Bin 0 -> 33275 bytes .../assets/document-templates/tr-TR/new.xlsx | Bin 0 -> 6341 bytes .../assets/document-templates/uk-UA/new.docx | Bin 0 -> 6945 bytes .../assets/document-templates/uk-UA/new.pdf | Bin 0 -> 10431 bytes .../assets/document-templates/uk-UA/new.pptx | Bin 0 -> 34725 bytes .../assets/document-templates/uk-UA/new.xlsx | Bin 0 -> 6320 bytes .../assets/document-templates/vi-VN/new.docx | Bin 0 -> 7839 bytes .../assets/document-templates/vi-VN/new.pdf | Bin 0 -> 10526 bytes .../assets/document-templates/vi-VN/new.pptx | Bin 0 -> 33595 bytes .../assets/document-templates/vi-VN/new.xlsx | Bin 0 -> 6311 bytes .../assets/document-templates/zh-CN/new.docx | Bin 0 -> 7839 bytes .../assets/document-templates/zh-CN/new.pdf | Bin 0 -> 10526 bytes .../assets/document-templates/zh-CN/new.pptx | Bin 0 -> 34707 bytes .../assets/document-templates/zh-CN/new.xlsx | Bin 0 -> 6322 bytes .../assets/document-templates/zh-TW/new.docx | Bin 0 -> 7839 bytes .../assets/document-templates/zh-TW/new.pdf | Bin 0 -> 10526 bytes .../assets/document-templates/zh-TW/new.pptx | Bin 0 -> 35545 bytes .../assets/document-templates/zh-TW/new.xlsx | Bin 0 -> 6352 bytes .../src/Manager/Document/DocumentManager.php | 288 + .../Document/DocumentManagerInterface.php | 214 + .../src/Manager/Formats/FormatsManager.php | 151 + .../Formats/FormatsManagerInterface.php | 66 + .../src/Manager/Security/JwtManager.php | 109 + .../Manager/Security/JwtManagerInterface.php | 59 + .../src/Manager/Settings/SettingsManager.php | 435 + .../Settings/SettingsManagerInterface.php | 150 + .../src/Models/Anonymous.php | 66 + .../src/Models/Callback.php | 223 + .../src/Models/CallbackAction.php | 66 + .../src/Models/CallbackActionType.php | 38 + .../src/Models/CallbackDocStatus.php | 41 + .../src/Models/CallbackForceSaveType.php | 38 + .../src/Models/Changes.php | 66 + .../src/Models/CoEditing.php | 67 + .../src/Models/CoEditingMode.php | 37 + .../src/Models/CommentGroups.php | 83 + .../src/Models/Config.php | 168 + .../src/Models/ConvertRequest.php | 258 + .../src/Models/ConvertRequestThumbnail.php | 103 + .../src/Models/Customer.php | 181 + .../src/Models/Customization.php | 586 + .../src/Models/DocEditorConfig.php | 276 + .../src/Models/Document.php | 169 + .../src/Models/DocumentType.php | 39 + .../src/Models/EditorsMode.php | 37 + .../src/Models/Embedded.php | 110 + .../src/Models/Features.php | 47 + .../src/Models/Format.php | 129 + .../src/Models/GoBack.php | 101 + .../src/Models/History.php | 58 + .../docs-integration-sdk/src/Models/Info.php | 125 + .../src/Models/JsonSerializable.php | 57 + .../docs-integration-sdk/src/Models/Logo.php | 101 + .../src/Models/MacrosMode.php | 38 + .../src/Models/Permissions.php | 353 + .../src/Models/Recent.php | 84 + .../src/Models/ReferenceData.php | 83 + .../src/Models/Review.php | 125 + .../src/Models/ReviewDisplay.php | 39 + .../src/Models/SharingSettings.php | 88 + .../src/Models/SharingSettingsPermissions.php | 38 + .../src/Models/Template.php | 84 + .../src/Models/Toolbar.php | 37 + .../docs-integration-sdk/src/Models/Type.php | 39 + .../docs-integration-sdk/src/Models/Unit.php | 39 + .../docs-integration-sdk/src/Models/User.php | 79 + .../src/Service/Callback/CallbackService.php | 117 + .../Callback/CallbackServiceInterface.php | 98 + .../DocEditorConfigService.php | 207 + .../DocEditorConfigServiceInterface.php | 174 + .../Service/Request/HttpClientInterface.php | 35 + .../src/Service/Request/RequestService.php | 456 + .../Request/RequestServiceInterface.php | 134 + .../src/Util/BasicEnum.php | 67 + .../src/Util/CommandResponseError.php | 61 + .../src/Util/CommonError.php | 97 + .../src/Util/ConvertResponseError.php | 64 + .../docs-integration-sdk/src/Util/EnvUtil.php | 50 + .../vendor/phpoption/phpoption/LICENSE | 201 + .../vendor/phpoption/phpoption/composer.json | 50 + .../phpoption/src/PhpOption/LazyOption.php | 175 + .../phpoption/src/PhpOption/None.php | 136 + .../phpoption/src/PhpOption/Option.php | 434 + .../phpoption/src/PhpOption/Some.php | 169 + .../vendor/symfony/polyfill-ctype/Ctype.php | 232 + .../vendor/symfony/polyfill-ctype/LICENSE | 19 + .../vendor/symfony/polyfill-ctype/README.md | 12 + .../symfony/polyfill-ctype/bootstrap.php | 50 + .../symfony/polyfill-ctype/bootstrap80.php | 46 + .../symfony/polyfill-ctype/composer.json | 38 + .../vendor/symfony/polyfill-mbstring/LICENSE | 19 + .../symfony/polyfill-mbstring/Mbstring.php | 1045 ++ .../symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/caseFolding.php | 119 + .../Resources/unidata/lowerCase.php | 1397 ++ .../Resources/unidata/titleCaseRegexp.php | 5 + .../Resources/unidata/upperCase.php | 1489 ++ .../symfony/polyfill-mbstring/bootstrap.php | 172 + .../symfony/polyfill-mbstring/bootstrap80.php | 167 + .../symfony/polyfill-mbstring/composer.json | 38 + .../vendor/symfony/polyfill-php80/LICENSE | 19 + .../vendor/symfony/polyfill-php80/Php80.php | 115 + .../symfony/polyfill-php80/PhpToken.php | 103 + .../vendor/symfony/polyfill-php80/README.md | 25 + .../Resources/stubs/Attribute.php | 31 + .../Resources/stubs/PhpToken.php | 16 + .../Resources/stubs/Stringable.php | 20 + .../Resources/stubs/UnhandledMatchError.php | 16 + .../Resources/stubs/ValueError.php | 16 + .../symfony/polyfill-php80/bootstrap.php | 42 + .../symfony/polyfill-php80/composer.json | 37 + .../vendor/vlucas/phpdotenv/LICENSE | 30 + .../vendor/vlucas/phpdotenv/composer.json | 60 + .../vendor/vlucas/phpdotenv/src/Dotenv.php | 267 + .../src/Exception/ExceptionInterface.php | 12 + .../Exception/InvalidEncodingException.php | 12 + .../src/Exception/InvalidFileException.php | 12 + .../src/Exception/InvalidPathException.php | 12 + .../src/Exception/ValidationException.php | 12 + .../vlucas/phpdotenv/src/Loader/Loader.php | 47 + .../phpdotenv/src/Loader/LoaderInterface.php | 20 + .../vlucas/phpdotenv/src/Loader/Resolver.php | 65 + .../vlucas/phpdotenv/src/Parser/Entry.php | 59 + .../phpdotenv/src/Parser/EntryParser.php | 300 + .../vlucas/phpdotenv/src/Parser/Lexer.php | 58 + .../vlucas/phpdotenv/src/Parser/Lines.php | 127 + .../vlucas/phpdotenv/src/Parser/Parser.php | 53 + .../phpdotenv/src/Parser/ParserInterface.php | 19 + .../vlucas/phpdotenv/src/Parser/Value.php | 88 + .../Repository/Adapter/AdapterInterface.php | 15 + .../src/Repository/Adapter/ApacheAdapter.php | 89 + .../src/Repository/Adapter/ArrayAdapter.php | 80 + .../Repository/Adapter/EnvConstAdapter.php | 89 + .../src/Repository/Adapter/GuardedWriter.php | 85 + .../Repository/Adapter/ImmutableWriter.php | 110 + .../src/Repository/Adapter/MultiReader.php | 48 + .../src/Repository/Adapter/MultiWriter.php | 64 + .../src/Repository/Adapter/PutenvAdapter.php | 91 + .../Repository/Adapter/ReaderInterface.php | 17 + .../Repository/Adapter/ReplacingWriter.php | 104 + .../Repository/Adapter/ServerConstAdapter.php | 89 + .../Repository/Adapter/WriterInterface.php | 27 + .../src/Repository/AdapterRepository.php | 107 + .../src/Repository/RepositoryBuilder.php | 272 + .../src/Repository/RepositoryInterface.php | 51 + .../vlucas/phpdotenv/src/Store/File/Paths.php | 44 + .../phpdotenv/src/Store/File/Reader.php | 81 + .../vlucas/phpdotenv/src/Store/FileStore.php | 72 + .../phpdotenv/src/Store/StoreBuilder.php | 141 + .../phpdotenv/src/Store/StoreInterface.php | 17 + .../phpdotenv/src/Store/StringStore.php | 37 + .../vlucas/phpdotenv/src/Util/Regex.php | 112 + .../vendor/vlucas/phpdotenv/src/Util/Str.php | 98 + .../vendor/vlucas/phpdotenv/src/Validator.php | 209 + plugin/openmeetings/README.md | 20 + plugin/openmeetings/config.php | 11 + plugin/openmeetings/course_index.php | 11 + plugin/openmeetings/index.php | 1 + plugin/openmeetings/install.php | 12 + plugin/openmeetings/lang/english.php | 55 + plugin/openmeetings/lang/french.php | 50 + plugin/openmeetings/lang/spanish.php | 53 + .../openmeetings/lib/openmeetings.class.php | 581 + plugin/openmeetings/lib/openmeetings_api.php | 25 + .../openmeetings/lib/openmeetings_gateway.php | 491 + .../lib/openmeetings_plugin.class.php | 104 + .../lib/openmeetings_rest_service.php | 186 + plugin/openmeetings/lib/room.class.php | 122 + plugin/openmeetings/lib/session.class.php | 18 + plugin/openmeetings/lib/user.class.php | 33 + plugin/openmeetings/listing.php | 118 + plugin/openmeetings/listing.tpl | 66 + plugin/openmeetings/plugin.php | 4 + plugin/openmeetings/start.php | 68 + plugin/openmeetings/uninstall.php | 14 + plugin/pausetraining/PauseTraining.php | 350 + plugin/pausetraining/README.md | 6 + plugin/pausetraining/config.php | 4 + plugin/pausetraining/cron.php | 7 + plugin/pausetraining/cronTest.php | 7 + plugin/pausetraining/index.php | 1 + plugin/pausetraining/install.php | 9 + plugin/pausetraining/lang/english.php | 20 + plugin/pausetraining/lang/french.php | 19 + plugin/pausetraining/lang/spanish.php | 1 + plugin/pausetraining/plugin.php | 4 + .../view/notification_content.tpl | 7 + plugin/pens/COPYING | 674 + plugin/pens/README.md | 29 + plugin/pens/chamilo_pens.php | 265 + plugin/pens/install.php | 13 + plugin/pens/lib/COPYING | 674 + plugin/pens/lib/PENSPlugin.php | 177 + plugin/pens/lib/README.textile | 79 + plugin/pens/lib/pens.php | 7 + plugin/pens/lib/pens/lib/rfc2396regexes.php | 113 + plugin/pens/lib/pens/pens_client.php | 113 + plugin/pens/lib/pens/pens_config.php | 69 + plugin/pens/lib/pens/pens_controller.php | 62 + plugin/pens/lib/pens/pens_exception.php | 98 + plugin/pens/lib/pens/pens_message.php | 5 + plugin/pens/lib/pens/pens_package_handler.php | 114 + plugin/pens/lib/pens/pens_request.php | 495 + plugin/pens/lib/pens/pens_request_alert.php | 48 + plugin/pens/lib/pens/pens_request_collect.php | 48 + plugin/pens/lib/pens/pens_request_factory.php | 68 + plugin/pens/lib/pens/pens_request_handler.php | 48 + plugin/pens/lib/pens/pens_request_receipt.php | 48 + plugin/pens/lib/pens/pens_response.php | 175 + plugin/pens/lib/pens/pens_server.php | 313 + plugin/pens/lib/pens_client_test.php | 39 + plugin/pens/lib/pens_server_test.php | 41 + .../lib/tests/test_pens_package_handler.php | 87 + plugin/pens/lib/tests/test_pens_request.php | 213 + plugin/pens/lib/tests/test_pens_response.php | 68 + plugin/pens/lib/tests/test_pens_server.php | 122 + plugin/pens/pens.php | 56 + plugin/pens/plugin.php | 36 + plugin/pens/uninstall.php | 15 + plugin/positioning/README.md | 10 + plugin/positioning/img/positioning-line.png | Bin 0 -> 16056 bytes plugin/positioning/img/positioning-line.svg | 810 + plugin/positioning/index.php | 1 + plugin/positioning/install.php | 7 + plugin/positioning/lang/english.php | 20 + plugin/positioning/lang/french.php | 20 + plugin/positioning/lang/spanish.php | 20 + plugin/positioning/plugin.php | 7 + plugin/positioning/src/Positioning.php | 260 + plugin/positioning/start.php | 151 + plugin/positioning/start_student.php | 110 + plugin/positioning/uninstall.php | 7 + plugin/positioning/view/start.tpl | 10 + plugin/positioning/view/start_student.tpl | 32 + .../QuestionOptionsEvaluationPlugin.php | 274 + plugin/questionoptionsevaluation/README.md | 20 + .../questionoptionsevaluation/evaluation.php | 74 + plugin/questionoptionsevaluation/install.php | 4 + .../lang/english.php | 19 + .../lang/spanish.php | 19 + plugin/questionoptionsevaluation/plugin.php | 4 + .../questionoptionsevaluation/uninstall.php | 4 + plugin/redirection/README.md | 18 + plugin/redirection/RedirectionPlugin.php | 180 + plugin/redirection/admin.php | 88 + plugin/redirection/config.php | 10 + plugin/redirection/index.php | 2 + plugin/redirection/install.php | 12 + plugin/redirection/lang/english.php | 5 + plugin/redirection/lang/french.php | 9 + plugin/redirection/lang/spanish.php | 5 + plugin/redirection/plugin.php | 13 + plugin/redirection/uninstall.php | 12 + plugin/remedial_course/README.md | 19 + .../remedial_course/RemedialCoursePlugin.php | 394 + plugin/remedial_course/index.php | 3 + plugin/remedial_course/install.php | 11 + plugin/remedial_course/lang/english.php | 13 + plugin/remedial_course/lang/spanish.php | 11 + plugin/remedial_course/plugin.php | 15 + plugin/remedial_course/uninstall.php | 8 + plugin/resubscription/README.md | 11 + plugin/resubscription/config.php | 10 + plugin/resubscription/index.php | 10 + plugin/resubscription/install.php | 12 + plugin/resubscription/lang/english.php | 15 + plugin/resubscription/lang/french.php | 13 + plugin/resubscription/lang/spanish.php | 15 + plugin/resubscription/plugin.php | 12 + .../resubscription/src/HookResubscription.php | 112 + plugin/resubscription/src/Resubscription.php | 78 + plugin/resubscription/uninstall.php | 12 + plugin/rss/README.md | 4 + plugin/rss/index.php | 35 + plugin/rss/lang/english.php | 16 + plugin/rss/lang/french.php | 11 + plugin/rss/lang/spanish.php | 9 + plugin/rss/lib/rss_plugin.class.php | 38 + plugin/rss/plugin.php | 3 + plugin/search_course/README.md | 4 + plugin/search_course/index.php | 4 + plugin/search_course/lang/english.php | 8 + plugin/search_course/lang/french.php | 8 + .../lib/register_course_widget.class.php | 144 + .../lib/search_course_plugin.class.php | 31 + .../lib/search_course_widget.class.php | 427 + plugin/search_course/plugin.php | 7 + plugin/send_notification_new_lp/README.md | 16 + .../SendNotificationToPublishLp.php | 174 + plugin/send_notification_new_lp/index.php | 3 + plugin/send_notification_new_lp/install.php | 10 + plugin/send_notification_new_lp/plugin.php | 20 + plugin/send_notification_new_lp/uninstall.php | 10 + plugin/sepe/CHANGELOG.md | 26 + plugin/sepe/README.md | 35 + plugin/sepe/admin.php | 14 + plugin/sepe/config.php | 14 + plugin/sepe/database.php | 900 + plugin/sepe/index.php | 12 + plugin/sepe/install.php | 14 + plugin/sepe/js/sepe.js | 223 + plugin/sepe/lang/english.php | 292 + plugin/sepe/lang/spanish.php | 292 + plugin/sepe/plugin.php | 14 + plugin/sepe/resources/button_delete.gif | Bin 0 -> 226 bytes plugin/sepe/resources/edit.png | Bin 0 -> 1598 bytes plugin/sepe/resources/folder.png | Bin 0 -> 2238 bytes plugin/sepe/resources/forms.png | Bin 0 -> 1253 bytes plugin/sepe/resources/icon-delete.png | Bin 0 -> 538 bytes plugin/sepe/resources/icon-edit.png | Bin 0 -> 627 bytes plugin/sepe/resources/list.png | Bin 0 -> 1308 bytes plugin/sepe/resources/options-lines.png | Bin 0 -> 711 bytes plugin/sepe/resources/plugin.css | 90 + plugin/sepe/resources/settings.png | Bin 0 -> 1278 bytes plugin/sepe/src/configuration.php | 43 + plugin/sepe/src/formative-action-edit.php | 234 + plugin/sepe/src/formative-action.php | 54 + plugin/sepe/src/formative-actions-list.php | 42 + plugin/sepe/src/function.php | 200 + plugin/sepe/src/identification-data-edit.php | 95 + plugin/sepe/src/identification-data.php | 36 + plugin/sepe/src/index.php | 1 + plugin/sepe/src/index.sepe.php | 41 + plugin/sepe/src/participant-action-edit.php | 248 + .../sepe/src/participant-specialty-edit.php | 284 + plugin/sepe/src/sepe-administration-menu.php | 59 + plugin/sepe/src/sepe.lib.php | 586 + plugin/sepe/src/sepe_plugin.class.php | 557 + plugin/sepe/src/specialty-action-edit.php | 241 + plugin/sepe/src/specialty-classroom-edit.php | 131 + plugin/sepe/src/specialty-tutor-edit.php | 232 + plugin/sepe/src/specialty-tutorial-edit.php | 158 + plugin/sepe/src/wsse/soap-server-wsse.php | 204 + plugin/sepe/src/wsse/soap-wsa.php | 168 + plugin/sepe/src/wsse/soap-wsse.php | 540 + plugin/sepe/uninstall.php | 11 + plugin/sepe/update.php | 14 + plugin/sepe/view/configuration.tpl | 33 + plugin/sepe/view/formative-action-edit.tpl | 300 + plugin/sepe/view/formative-action.tpl | 269 + plugin/sepe/view/formative-actions-list.tpl | 71 + plugin/sepe/view/identification-data-edit.tpl | 86 + plugin/sepe/view/identification-data.tpl | 96 + plugin/sepe/view/participant-action-edit.tpl | 320 + .../sepe/view/participant-specialty-edit.tpl | 322 + plugin/sepe/view/sepe-administration-menu.tpl | 5 + plugin/sepe/view/specialty-action-edit.tpl | 365 + plugin/sepe/view/specialty-classroom-edit.tpl | 94 + plugin/sepe/view/specialty-tutor-edit.tpl | 285 + plugin/sepe/view/specialty-tutorial-edit.tpl | 127 + plugin/sepe/ws/ProveedorCentroTFWS.dist.wsdl | 913 + plugin/sepe/ws/Sepe.php | 1374 ++ plugin/sepe/ws/service.php | 149 + plugin/share_buttons/README.md | 6 + plugin/share_buttons/index.php | 21 + plugin/share_buttons/plugin.php | 22 + plugin/show_regions/README.md | 4 + plugin/show_regions/index.php | 9 + plugin/show_regions/plugin.php | 21 + plugin/show_user_info/README.md | 5 + plugin/show_user_info/index.php | 17 + plugin/show_user_info/lang/english.php | 3 + plugin/show_user_info/lang/spanish.php | 3 + plugin/show_user_info/plugin.php | 23 + plugin/show_user_info/template.tpl | 30 + plugin/static/README.md | 4 + plugin/static/index.php | 25 + plugin/static/lang/english.php | 11 + plugin/static/lang/french.php | 9 + plugin/static/lang/spanish.php | 9 + plugin/static/lib/static_plugin.class.php | 36 + plugin/static/plugin.php | 8 + plugin/static/resources/static.css | 4 + plugin/studentfollowup/Entity/CarePost.php | 435 + plugin/studentfollowup/README.md | 15 + .../studentfollowup/StudentFollowUpPlugin.php | 245 + plugin/studentfollowup/demo_content.php | 28 + plugin/studentfollowup/index.php | 2 + plugin/studentfollowup/install.php | 7 + plugin/studentfollowup/lang/dutch.php | 10 + plugin/studentfollowup/lang/english.php | 8 + plugin/studentfollowup/my_students.php | 203 + plugin/studentfollowup/plugin.php | 20 + plugin/studentfollowup/post.php | 138 + plugin/studentfollowup/posts.php | 84 + plugin/studentfollowup/uninstall.php | 8 + .../view/my_students.html.twig | 26 + plugin/studentfollowup/view/post.html.twig | 99 + plugin/studentfollowup/view/posts.html.twig | 11 + plugin/surveyexportcsv/README.md | 42 + .../surveyexportcsv/SurveyExportCsvPlugin.php | 95 + plugin/surveyexportcsv/export.php | 216 + plugin/surveyexportcsv/install.php | 4 + plugin/surveyexportcsv/lang/english.php | 9 + plugin/surveyexportcsv/lang/spanish.php | 9 + plugin/surveyexportcsv/plugin.php | 4 + plugin/surveyexportcsv/start.php | 50 + plugin/surveyexportcsv/uninstall.php | 4 + plugin/surveyexporttxt/README.md | 17 + .../surveyexporttxt/SurveyExportTxtPlugin.php | 95 + plugin/surveyexporttxt/export.php | 203 + plugin/surveyexporttxt/install.php | 4 + plugin/surveyexporttxt/lang/english.php | 9 + plugin/surveyexporttxt/lang/spanish.php | 9 + plugin/surveyexporttxt/plugin.php | 4 + plugin/surveyexporttxt/start.php | 50 + plugin/surveyexporttxt/uninstall.php | 4 + plugin/test2pdf/README.md | 11 + plugin/test2pdf/config.php | 12 + plugin/test2pdf/index.php | 7 + plugin/test2pdf/install.php | 16 + plugin/test2pdf/lang/english.php | 15 + plugin/test2pdf/lang/french.php | 15 + plugin/test2pdf/lang/spanish.php | 15 + plugin/test2pdf/plugin.php | 13 + plugin/test2pdf/resources/css/style.css | 3 + plugin/test2pdf/resources/img/22/test2pdf.png | Bin 0 -> 829 bytes plugin/test2pdf/resources/img/32/test2pdf.png | Bin 0 -> 2908 bytes .../test2pdf/resources/img/32/test2pdf_na.png | Bin 0 -> 2004 bytes plugin/test2pdf/resources/img/64/test2pdf.png | Bin 0 -> 1096 bytes .../test2pdf/resources/img/64/test2pdf_na.png | Bin 0 -> 1006 bytes plugin/test2pdf/src/download-pdf.php | 173 + plugin/test2pdf/src/index.php | 1 + plugin/test2pdf/src/index.test2pdf.php | 7 + plugin/test2pdf/src/test2pdf.lib.php | 434 + plugin/test2pdf/src/test2pdf_plugin.class.php | 77 + plugin/test2pdf/src/view-pdf.php | 35 + plugin/test2pdf/start.php | 28 + plugin/test2pdf/uninstall.php | 14 + plugin/test2pdf/view/view-pdf.tpl | 44 + plugin/toplinks/README.md | 9 + plugin/toplinks/admin.php | 259 + plugin/toplinks/index.php | 47 + plugin/toplinks/install.php | 5 + plugin/toplinks/lang/english.php | 9 + plugin/toplinks/lang/spanish.php | 9 + plugin/toplinks/plugin.php | 5 + .../Repository/TopLinkRelToolRepository.php | 78 + .../Entity/Repository/TopLinkRepository.php | 26 + plugin/toplinks/src/Entity/TopLink.php | 137 + plugin/toplinks/src/Entity/TopLinkRelTool.php | 84 + .../Hook/TopLinksCreateCourseHookObserver.php | 40 + plugin/toplinks/src/LinkForm.php | 148 + plugin/toplinks/src/TopLinksPlugin.php | 136 + plugin/toplinks/start.php | 29 + plugin/toplinks/uninstall.php | 5 + plugin/tour/README.md | 31 + plugin/tour/ajax/save.ajax.php | 25 + plugin/tour/ajax/steps.ajax.php | 34 + plugin/tour/config.php | 15 + plugin/tour/config/tour.json | 117 + plugin/tour/index.php | 48 + plugin/tour/install.php | 12 + plugin/tour/intro.js/intro.min.js | 27 + plugin/tour/intro.js/introjs-nassim.css | 288 + plugin/tour/intro.js/introjs-nazanin.css | 257 + plugin/tour/intro.js/introjs-royal.css | 274 + plugin/tour/intro.js/introjs-rtl.min.css | 1 + plugin/tour/intro.js/introjs.min.css | 289 + plugin/tour/lang/brazilian.php | 65 + plugin/tour/lang/english.php | 65 + plugin/tour/lang/french.php | 65 + plugin/tour/lang/spanish.php | 65 + plugin/tour/plugin.php | 14 + plugin/tour/resources/fonts/lato-black.eot | Bin 0 -> 30838 bytes plugin/tour/resources/fonts/lato-black.svg | 4691 +++++ plugin/tour/resources/fonts/lato-black.ttf | Bin 0 -> 65316 bytes plugin/tour/resources/fonts/lato-black.woff | Bin 0 -> 35048 bytes plugin/tour/resources/fonts/lato-bold.eot | Bin 0 -> 32056 bytes plugin/tour/resources/fonts/lato-bold.svg | 5085 ++++++ plugin/tour/resources/fonts/lato-bold.ttf | Bin 0 -> 67592 bytes plugin/tour/resources/fonts/lato-bold.woff | Bin 0 -> 36664 bytes .../tour/resources/fonts/lato-bolditalic.eot | Bin 0 -> 30340 bytes .../tour/resources/fonts/lato-bolditalic.svg | 4514 +++++ .../tour/resources/fonts/lato-bolditalic.ttf | Bin 0 -> 58908 bytes .../tour/resources/fonts/lato-bolditalic.woff | Bin 0 -> 33916 bytes plugin/tour/resources/fonts/lato-italic.eot | Bin 0 -> 29887 bytes plugin/tour/resources/fonts/lato-italic.svg | 4514 +++++ plugin/tour/resources/fonts/lato-italic.ttf | Bin 0 -> 56732 bytes plugin/tour/resources/fonts/lato-italic.woff | Bin 0 -> 33592 bytes plugin/tour/resources/fonts/lato-light.eot | Bin 0 -> 30116 bytes plugin/tour/resources/fonts/lato-light.svg | 4691 +++++ plugin/tour/resources/fonts/lato-light.ttf | Bin 0 -> 64540 bytes plugin/tour/resources/fonts/lato-light.woff | Bin 0 -> 34608 bytes plugin/tour/resources/fonts/lato-regular.eot | Bin 0 -> 30978 bytes plugin/tour/resources/fonts/lato-regular.svg | 4691 +++++ plugin/tour/resources/fonts/lato-regular.ttf | Bin 0 -> 65648 bytes plugin/tour/resources/fonts/lato-regular.woff | Bin 0 -> 35464 bytes plugin/tour/resources/tour-chamilo.png | Bin 0 -> 3112 bytes plugin/tour/src/tour_plugin.class.php | 141 + plugin/tour/uninstall.php | 12 + plugin/tour/views/script.tpl | 88 + .../Entity/UserRemoteService.php | 172 + plugin/userremoteservice/README.md | 10 + plugin/userremoteservice/admin.php | 18 + plugin/userremoteservice/config.php | 4 + plugin/userremoteservice/iframe.php | 16 + plugin/userremoteservice/index.php | 10 + plugin/userremoteservice/install.php | 6 + plugin/userremoteservice/lang/english.php | 24 + plugin/userremoteservice/lang/french.php | 26 + plugin/userremoteservice/plugin.php | 6 + plugin/userremoteservice/redirect.php | 13 + .../src/user_remote_service_plugin.class.php | 309 + plugin/userremoteservice/uninstall.php | 6 + plugin/vchamilo/README.md | 142 + plugin/vchamilo/ajax/service.php | 25 + plugin/vchamilo/cli/bulkcreatenodes.php | 218 + plugin/vchamilo/cli/bulkdestroynodes.php | 170 + plugin/vchamilo/cli/clilib.php | 343 + plugin/vchamilo/cli/cron.php | 77 + plugin/vchamilo/cli/maincron.php | 25 + plugin/vchamilo/cli/nodelist-dist.csv | 12 + plugin/vchamilo/index.php | 27 + plugin/vchamilo/install.php | 79 + plugin/vchamilo/js/ajax.js | 16 + plugin/vchamilo/js/host_form.js | 62 + plugin/vchamilo/js/host_list.js | 15 + plugin/vchamilo/lang/english.php | 123 + plugin/vchamilo/lang/french.php | 108 + plugin/vchamilo/lang/spanish.php | 5 + plugin/vchamilo/lib/VChamiloPlugin.php | 39 + plugin/vchamilo/lib/Virtual.php | 1388 ++ plugin/vchamilo/pix/ajax_waiter.gif | Bin 0 -> 14190 bytes plugin/vchamilo/pix/icon.gif | Bin 0 -> 205 bytes plugin/vchamilo/pix/logo.png | Bin 0 -> 7278 bytes plugin/vchamilo/plugin.php | 106 + plugin/vchamilo/template.tpl | 10 + plugin/vchamilo/templates/README.txt | 2 + plugin/vchamilo/uninstall.php | 9 + plugin/vchamilo/vcron.php | 294 + plugin/vchamilo/views/editinstance.php | 78 + plugin/vchamilo/views/editinstance_form.php | 579 + plugin/vchamilo/views/import.php | 196 + plugin/vchamilo/views/manage.controller.php | 373 + plugin/vchamilo/views/manage.php | 157 + plugin/vchamilo/views/manage.testcnx.php | 55 + plugin/vchamilo/views/manage.testdatapath.php | 3 + .../vchamilo/views/syncparams.controller.php | 132 + plugin/vchamilo/views/syncparams.php | 83 + plugin/vchamilo/views/upgrade.php | 65 + .../Controller/AuthenticationController.php | 366 + .../Controller/BaseController.php | 67 + .../Controller/EnrollmentController.php | 116 + plugin/whispeakauth/Entity/LogEvent.php | 145 + plugin/whispeakauth/Entity/LogEventLp.php | 84 + plugin/whispeakauth/Entity/LogEventQuiz.php | 84 + plugin/whispeakauth/README.md | 23 + plugin/whispeakauth/Request/ApiRequest.php | 230 + plugin/whispeakauth/WhispeakAuthPlugin.php | 804 + .../WhispeakConditionalLoginHook.php | 59 + .../WhispeakMyStudentsLpTrackingHook.php | 67 + .../WhispeakMyStudentsQuizTrackingHook.php | 84 + plugin/whispeakauth/admin.php | 149 + .../whispeakauth/ajax/authentify_password.php | 140 + plugin/whispeakauth/ajax/record_audio.php | 48 + plugin/whispeakauth/assets/js/RecordAudio.js | 191 + plugin/whispeakauth/authentify.php | 17 + plugin/whispeakauth/authentify_password.php | 79 + plugin/whispeakauth/enrollment.php | 21 + plugin/whispeakauth/index.php | 14 + plugin/whispeakauth/install.php | 4 + plugin/whispeakauth/lang/english.php | 48 + plugin/whispeakauth/lang/french.php | 48 + plugin/whispeakauth/lang/spanish.php | 48 + plugin/whispeakauth/plugin.php | 4 + plugin/whispeakauth/revocation.php | 94 + plugin/whispeakauth/uninstall.php | 4 + .../view/authentify_password.html.twig | 44 + .../view/authentify_recorder.html.twig | 44 + .../whispeakauth/view/quiz_question.html.twig | 45 + .../whispeakauth/view/record_audio.html.twig | 59 + plugin/xapi/.htaccess | 1 + plugin/xapi/README.md | 88 + plugin/xapi/admin.php | 196 + plugin/xapi/assets/css/cmi5_launch.css | 40 + plugin/xapi/assets/js/cmi5_launch.js | 2 + plugin/xapi/cmi5/launch.php | 188 + plugin/xapi/cmi5/token.php | 22 + plugin/xapi/cmi5/view.php | 84 + plugin/xapi/cron/send_statements.php | 79 + plugin/xapi/install.php | 5 + plugin/xapi/lang/english.php | 56 + plugin/xapi/lang/french.php | 49 + plugin/xapi/lang/spanish.php | 56 + plugin/xapi/lrs.php | 12 + plugin/xapi/php-xapi/lrs-bundle/LICENSE | 19 + plugin/xapi/php-xapi/lrs-bundle/README.md | 0 plugin/xapi/php-xapi/lrs-bundle/composer.json | 51 + .../src/Controller/StatementGetController.php | 209 + .../Controller/StatementHeadController.php | 27 + .../Controller/StatementPostController.php | 67 + .../src/Controller/StatementPutController.php | 65 + .../src/DependencyInjection/Configuration.php | 35 + .../DependencyInjection/XApiLrsExtension.php | 60 + .../AlternateRequestSyntaxListener.php | 73 + .../src/EventListener/ExceptionListener.php | 17 + .../src/EventListener/SerializerListener.php | 40 + .../src/EventListener/VersionListener.php | 66 + .../src/Model/StatementsFilterFactory.php | 86 + .../src/Resources/config/controller.xml | 21 + .../src/Resources/config/doctrine.xml | 12 + .../src/Resources/config/event_listener.xml | 25 + .../src/Resources/config/factory.xml | 12 + .../lrs-bundle/src/Resources/config/orm.xml | 18 + .../src/Resources/config/routing.xml | 29 + .../src/Resources/config/serializer.xml | 32 + .../src/Response/AttachmentResponse.php | 76 + .../src/Response/MultipartResponse.php | 135 + .../php-xapi/lrs-bundle/src/XApiLrsBundle.php | 26 + .../php-xapi/repository-doctrine-orm/LICENSE | 19 + .../repository-doctrine-orm/README.md | 0 .../repository-doctrine-orm/composer.json | 41 + .../metadata/Actor.orm.xml | 19 + .../metadata/Attachment.orm.xml | 23 + .../metadata/Context.orm.xml | 59 + .../metadata/Extensions.orm.xml | 13 + .../metadata/Result.orm.xml | 28 + .../metadata/Statement.orm.xml | 62 + .../metadata/StatementObject.orm.xml | 83 + .../metadata/Verb.orm.xml | 14 + .../src/StatementRepository.php | 67 + plugin/xapi/plugin.php | 5 + plugin/xapi/src/Entity/ActivityProfile.php | 93 + plugin/xapi/src/Entity/ActivityState.php | 111 + plugin/xapi/src/Entity/Cmi5Item.php | 372 + plugin/xapi/src/Entity/InternalLog.php | 227 + plugin/xapi/src/Entity/LrsAuth.php | 104 + .../Repository/ToolLaunchRepository.php | 85 + plugin/xapi/src/Entity/SharedStatement.php | 105 + plugin/xapi/src/Entity/ToolLaunch.php | 295 + .../src/Hook/XApiActivityHookObserver.php | 66 + .../src/Hook/XApiCreateCourseHookObserver.php | 37 + .../Hook/XApiLearningPathEndHookObserver.php | 26 + ...XApiLearningPathItemViewedHookObserver.php | 35 + ...XApiPortfolioCommentEditedHookObserver.php | 19 + ...XApiPortfolioCommentScoredHookObserver.php | 19 + .../XApiPortfolioDownloadedHookObserver.php | 17 + .../XApiPortfolioItemAddedHookObserver.php | 25 + ...XApiPortfolioItemCommentedHookObserver.php | 25 + .../XApiPortfolioItemEditedHookObserver.php | 19 + ...piPortfolioItemHighlightedHookObserver.php | 22 + .../XApiPortfolioItemScoredHookObserver.php | 19 + .../XApiPortfolioItemViewedHookObserver.php | 25 + .../xapi/src/Hook/XApiQuizEndHookObserver.php | 29 + .../XApiQuizQuestionAnsweredHookObserver.php | 42 + plugin/xapi/src/Importer/PackageImporter.php | 67 + .../xapi/src/Importer/XmlPackageImporter.php | 29 + .../xapi/src/Importer/ZipPackageImporter.php | 88 + plugin/xapi/src/Lrs/AboutController.php | 30 + .../src/Lrs/ActivitiesProfileController.php | 78 + .../src/Lrs/ActivitiesStateController.php | 120 + plugin/xapi/src/Lrs/BaseController.php | 28 + plugin/xapi/src/Lrs/LrsRequest.php | 221 + plugin/xapi/src/Lrs/StatementsController.php | 147 + plugin/xapi/src/Lrs/Utils/InternalLogUtil.php | 115 + plugin/xapi/src/Parser/Cmi5Parser.php | 178 + plugin/xapi/src/Parser/PackageParser.php | 60 + plugin/xapi/src/Parser/TinCanParser.php | 69 + .../ToolExperience/Activity/BaseActivity.php | 49 + .../src/ToolExperience/Activity/Course.php | 40 + .../ToolExperience/Activity/LearningPath.php | 53 + .../Activity/LearningPathItem.php | 54 + .../src/ToolExperience/Activity/Portfolio.php | 50 + .../Activity/PortfolioCategory.php | 58 + .../Activity/PortfolioComment.php | 50 + .../ToolExperience/Activity/PortfolioItem.php | 49 + .../xapi/src/ToolExperience/Activity/Quiz.php | 57 + .../ToolExperience/Activity/QuizQuestion.php | 170 + .../xapi/src/ToolExperience/Activity/Site.php | 33 + .../src/ToolExperience/Actor/BaseActor.php | 17 + plugin/xapi/src/ToolExperience/Actor/User.php | 35 + .../Statement/BaseStatement.php | 53 + .../Statement/LearningPathCompleted.php | 61 + .../Statement/LearningPathItemViewed.php | 79 + .../Statement/PortfolioAttachmentsTrait.php | 89 + .../Statement/PortfolioComment.php | 25 + .../Statement/PortfolioCommentEdited.php | 39 + .../Statement/PortfolioCommentScored.php | 47 + .../Statement/PortfolioDownloaded.php | 42 + .../Statement/PortfolioItem.php | 41 + .../Statement/PortfolioItemCommented.php | 74 + .../Statement/PortfolioItemEdited.php | 39 + .../Statement/PortfolioItemHighlighted.php | 39 + .../Statement/PortfolioItemScored.php | 47 + .../Statement/PortfolioItemShared.php | 47 + .../Statement/PortfolioItemViewed.php | 40 + .../Statement/QuizCompleted.php | 70 + .../Statement/QuizQuestionAnswered.php | 79 + .../xapi/src/ToolExperience/Verb/Answered.php | 21 + .../xapi/src/ToolExperience/Verb/BaseVerb.php | 46 + .../src/ToolExperience/Verb/Commented.php | 21 + .../src/ToolExperience/Verb/Completed.php | 21 + .../src/ToolExperience/Verb/Downloaded.php | 14 + .../xapi/src/ToolExperience/Verb/Edited.php | 16 + .../src/ToolExperience/Verb/Highlighted.php | 16 + .../xapi/src/ToolExperience/Verb/Replied.php | 21 + .../xapi/src/ToolExperience/Verb/Scored.php | 16 + .../xapi/src/ToolExperience/Verb/Shared.php | 21 + .../xapi/src/ToolExperience/Verb/Viewed.php | 21 + plugin/xapi/src/XApiPlugin.php | 671 + plugin/xapi/start.php | 151 + plugin/xapi/tincan/launch.php | 142 + plugin/xapi/tincan/stats.php | 163 + plugin/xapi/tincan/stats_attempts.ajax.php | 131 + plugin/xapi/tincan/stats_statements.ajax.php | 113 + plugin/xapi/tincan/view.php | 192 + plugin/xapi/tool_delete.php | 39 + plugin/xapi/tool_edit.php | 145 + plugin/xapi/tool_import.php | 177 + plugin/xapi/uninstall.php | 5 + plugin/xapi/views/cmi5_launch.twig | 15 + plugin/zoom/Entity/Meeting.php | 706 + plugin/zoom/Entity/MeetingActivity.php | 208 + plugin/zoom/Entity/Presenter.php | 38 + plugin/zoom/Entity/Recording.php | 193 + plugin/zoom/Entity/Registrant.php | 283 + plugin/zoom/Entity/Signature.php | 84 + plugin/zoom/Entity/Webinar.php | 138 + plugin/zoom/README.code.md | 70 + plugin/zoom/README.md | 148 + plugin/zoom/activity.php | 55 + plugin/zoom/attendance.php | 181 + plugin/zoom/calendar.ajax.php | 66 + plugin/zoom/calendar.php | 47 + plugin/zoom/config.php | 5 + plugin/zoom/endpoint.php | 176 + plugin/zoom/global.php | 13 + plugin/zoom/index.php | 1 + plugin/zoom/install.php | 8 + plugin/zoom/join_meeting.php | 114 + plugin/zoom/lang/english.php | 191 + plugin/zoom/lang/french.php | 173 + plugin/zoom/lang/spanish.php | 174 + plugin/zoom/lib/API/Api2RequestTrait.php | 67 + plugin/zoom/lib/API/BaseMeetingTrait.php | 41 + plugin/zoom/lib/API/Client.php | 56 + plugin/zoom/lib/API/CreatedRegistration.php | 42 + plugin/zoom/lib/API/CustomQuestion.php | 18 + plugin/zoom/lib/API/FollowUpUsers.php | 26 + plugin/zoom/lib/API/GlobalDialInNumber.php | 39 + plugin/zoom/lib/API/JWTClient.php | 38 + .../zoom/lib/API/JsonDeserializableTrait.php | 122 + plugin/zoom/lib/API/Meeting.php | 88 + plugin/zoom/lib/API/MeetingInfo.php | 34 + plugin/zoom/lib/API/MeetingInfoGet.php | 148 + plugin/zoom/lib/API/MeetingInstance.php | 50 + plugin/zoom/lib/API/MeetingInstances.php | 53 + plugin/zoom/lib/API/MeetingList.php | 58 + plugin/zoom/lib/API/MeetingListItem.php | 44 + plugin/zoom/lib/API/MeetingRegistrant.php | 22 + plugin/zoom/lib/API/MeetingRegistrantList.php | 53 + .../lib/API/MeetingRegistrantListItem.php | 29 + plugin/zoom/lib/API/MeetingSettings.php | 143 + plugin/zoom/lib/API/Ocurrence.php | 22 + plugin/zoom/lib/API/Pagination.php | 68 + plugin/zoom/lib/API/PaginationToken.php | 71 + plugin/zoom/lib/API/ParticipantList.php | 57 + plugin/zoom/lib/API/ParticipantListItem.php | 22 + plugin/zoom/lib/API/PastMeeting.php | 74 + plugin/zoom/lib/API/QuestionAndAnswer.php | 23 + plugin/zoom/lib/API/RecordingFile.php | 115 + plugin/zoom/lib/API/RecordingList.php | 65 + plugin/zoom/lib/API/RecordingMeeting.php | 95 + plugin/zoom/lib/API/RegistrantSchema.php | 103 + .../lib/API/ServerToServerOAuthClient.php | 84 + plugin/zoom/lib/API/TrackingField.php | 29 + .../zoom/lib/API/WebinarRegistrantSchema.php | 9 + plugin/zoom/lib/API/WebinarSchema.php | 137 + plugin/zoom/lib/API/WebinarSettings.php | 201 + plugin/zoom/lib/MeetingRepository.php | 215 + plugin/zoom/lib/RecordingRepository.php | 65 + plugin/zoom/lib/RegistrantRepository.php | 49 + plugin/zoom/lib/ZoomPlugin.php | 2000 ++ plugin/zoom/meeting.ajax.php | 50 + plugin/zoom/meeting.php | 110 + plugin/zoom/meetings.php | 28 + plugin/zoom/plugin.php | 4 + plugin/zoom/resources/img/22/zoom_meet.png | Bin 0 -> 4640 bytes plugin/zoom/resources/img/22/zoom_meet_na.png | Bin 0 -> 3895 bytes plugin/zoom/resources/img/32/zoom_meet.png | Bin 0 -> 2686 bytes plugin/zoom/resources/img/32/zoom_meet_na.png | Bin 0 -> 2065 bytes plugin/zoom/resources/img/64/zoom_meet.png | Bin 0 -> 6382 bytes plugin/zoom/resources/img/64/zoom_meet_na.png | Bin 0 -> 2917 bytes plugin/zoom/resources/img/svg/zoommeet.svg | 291 + plugin/zoom/start.php | 118 + plugin/zoom/subscription.php | 117 + plugin/zoom/uninstall.php | 8 + plugin/zoom/view/activity.tpl | 57 + plugin/zoom/view/calendar.tpl | 324 + plugin/zoom/view/join.tpl | 196 + plugin/zoom/view/meeting.tpl | 136 + plugin/zoom/view/meeting_details.tpl | 23 + plugin/zoom/view/meetings.tpl | 64 + plugin/zoom/view/start.tpl | 60 + plugin/zoom/view/subscription.tpl | 3 + 3370 files changed, 327179 insertions(+) create mode 100644 plugin/add_cas_login_button/README.md create mode 100644 plugin/add_cas_login_button/css.css create mode 100644 plugin/add_cas_login_button/index.php create mode 100644 plugin/add_cas_login_button/plugin.php create mode 100644 plugin/add_cas_login_button/template.tpl create mode 100644 plugin/add_cas_logout_button/README.md create mode 100644 plugin/add_cas_logout_button/css.css create mode 100644 plugin/add_cas_logout_button/index.php create mode 100644 plugin/add_cas_logout_button/plugin.php create mode 100644 plugin/add_cas_logout_button/template.tpl create mode 100644 plugin/add_facebook_login_button/README.md create mode 100644 plugin/add_facebook_login_button/css.css create mode 100644 plugin/add_facebook_login_button/index.php create mode 100644 plugin/add_facebook_login_button/plugin.php create mode 100644 plugin/add_facebook_login_button/template.tpl create mode 100644 plugin/add_shibboleth_login_button/README.md create mode 100644 plugin/add_shibboleth_login_button/css.css create mode 100644 plugin/add_shibboleth_login_button/index.php create mode 100644 plugin/add_shibboleth_login_button/plugin.php create mode 100644 plugin/add_shibboleth_login_button/template.tpl create mode 100644 plugin/advanced_subscription/README.md create mode 100644 plugin/advanced_subscription/ajax/advanced_subscription.ajax.php create mode 100644 plugin/advanced_subscription/config.php create mode 100644 plugin/advanced_subscription/cron/notify_by_mail.php create mode 100644 plugin/advanced_subscription/index.html create mode 100644 plugin/advanced_subscription/install.php create mode 100644 plugin/advanced_subscription/lang/english.php create mode 100644 plugin/advanced_subscription/lang/french.php create mode 100644 plugin/advanced_subscription/lang/spanish.php create mode 100644 plugin/advanced_subscription/license.txt create mode 100644 plugin/advanced_subscription/plugin.php create mode 100644 plugin/advanced_subscription/src/AdvancedSubscriptionPlugin.php create mode 100644 plugin/advanced_subscription/src/HookAdvancedSubscription.php create mode 100644 plugin/advanced_subscription/src/admin_view.php create mode 100644 plugin/advanced_subscription/src/open_session.php create mode 100644 plugin/advanced_subscription/src/render_mail.php create mode 100644 plugin/advanced_subscription/src/scripts/insert_session_fields.php create mode 100644 plugin/advanced_subscription/src/scripts/insert_sessions_categories.php create mode 100644 plugin/advanced_subscription/src/terms_and_conditions.php create mode 100644 plugin/advanced_subscription/test/mails.php create mode 100644 plugin/advanced_subscription/test/terms_to_pdf.php create mode 100644 plugin/advanced_subscription/test/ws_session_user.php create mode 100644 plugin/advanced_subscription/uninstall.php create mode 100644 plugin/advanced_subscription/views/admin_accepted_notice_admin.tpl create mode 100644 plugin/advanced_subscription/views/admin_accepted_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/admin_accepted_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/admin_rejected_notice_admin.tpl create mode 100644 plugin/advanced_subscription/views/admin_rejected_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/admin_rejected_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/admin_view.tpl create mode 100644 plugin/advanced_subscription/views/css/style.css create mode 100644 plugin/advanced_subscription/views/img/aprobar.png create mode 100644 plugin/advanced_subscription/views/img/avatar.png create mode 100644 plugin/advanced_subscription/views/img/desaprobar.png create mode 100644 plugin/advanced_subscription/views/img/footer.png create mode 100644 plugin/advanced_subscription/views/img/header.png create mode 100644 plugin/advanced_subscription/views/img/icon-avatar.png create mode 100644 plugin/advanced_subscription/views/img/line.png create mode 100644 plugin/advanced_subscription/views/open_session.tpl create mode 100644 plugin/advanced_subscription/views/reminder_notice_admin.tpl create mode 100644 plugin/advanced_subscription/views/reminder_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/reminder_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/reminder_notice_superior_max.tpl create mode 100644 plugin/advanced_subscription/views/student_no_superior_notice_admin.tpl create mode 100644 plugin/advanced_subscription/views/student_no_superior_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/student_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/student_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/superior_accepted_notice_admin.tpl create mode 100644 plugin/advanced_subscription/views/superior_accepted_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/superior_accepted_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/superior_rejected_notice_student.tpl create mode 100644 plugin/advanced_subscription/views/superior_rejected_notice_superior.tpl create mode 100644 plugin/advanced_subscription/views/terms_and_conditions.tpl create mode 100644 plugin/advanced_subscription/views/terms_and_conditions_to_pdf.tpl create mode 100644 plugin/ai_helper/AiHelperPlugin.php create mode 100644 plugin/ai_helper/Entity/Requests.php create mode 100644 plugin/ai_helper/README.md create mode 100644 plugin/ai_helper/install.php create mode 100644 plugin/ai_helper/lang/english.php create mode 100644 plugin/ai_helper/lang/french.php create mode 100644 plugin/ai_helper/lang/spanish.php create mode 100644 plugin/ai_helper/plugin.php create mode 100644 plugin/ai_helper/src/deepseek/DeepSeek.php create mode 100644 plugin/ai_helper/src/deepseek/DeepSeekUrl.php create mode 100644 plugin/ai_helper/src/openai/OpenAi.php create mode 100644 plugin/ai_helper/src/openai/OpenAiUrl.php create mode 100644 plugin/ai_helper/tool/answers.php create mode 100644 plugin/ai_helper/tool/learnpath.php create mode 100644 plugin/ai_helper/uninstall.php create mode 100644 plugin/azure_active_directory/CHANGELOG.md create mode 100644 plugin/azure_active_directory/README.md create mode 100644 plugin/azure_active_directory/index.php create mode 100644 plugin/azure_active_directory/install.php create mode 100644 plugin/azure_active_directory/lang/dutch.php create mode 100644 plugin/azure_active_directory/lang/english.php create mode 100644 plugin/azure_active_directory/lang/french.php create mode 100644 plugin/azure_active_directory/lang/spanish.php create mode 100644 plugin/azure_active_directory/layout/login_form.tpl create mode 100644 plugin/azure_active_directory/login.php create mode 100644 plugin/azure_active_directory/plugin.php create mode 100644 plugin/azure_active_directory/src/AzureActiveDirectory.php create mode 100644 plugin/azure_active_directory/src/AzureCommand.php create mode 100644 plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php create mode 100644 plugin/azure_active_directory/src/AzureSyncUsersCommand.php create mode 100644 plugin/azure_active_directory/src/Entity/AzureSyncState.php create mode 100644 plugin/azure_active_directory/src/callback.php create mode 100644 plugin/azure_active_directory/src/scripts/sync_usergroups.php create mode 100644 plugin/azure_active_directory/src/scripts/sync_users.php create mode 100644 plugin/azure_active_directory/uninstall.php create mode 100644 plugin/azure_active_directory/view/block.tpl create mode 100644 plugin/azure_active_directory/view/login.tpl create mode 100644 plugin/bbb/CHANGELOG.md create mode 100644 plugin/bbb/README.md create mode 100644 plugin/bbb/admin.php create mode 100644 plugin/bbb/ajax.php create mode 100644 plugin/bbb/config.php create mode 100644 plugin/bbb/config.vm.dist.php create mode 100644 plugin/bbb/course_index.php create mode 100644 plugin/bbb/cron.php create mode 100644 plugin/bbb/cron_close_meeting.php create mode 100644 plugin/bbb/index.php create mode 100644 plugin/bbb/install.php create mode 100644 plugin/bbb/lang/english.php create mode 100644 plugin/bbb/lang/french.php create mode 100644 plugin/bbb/lang/german.php create mode 100644 plugin/bbb/lang/spanish.php create mode 100644 plugin/bbb/lib/VM.php create mode 100644 plugin/bbb/lib/bbb.lib.php create mode 100644 plugin/bbb/lib/bbb_api.php create mode 100644 plugin/bbb/lib/bbb_plugin.class.php create mode 100644 plugin/bbb/lib/vm/AbstractVM.php create mode 100644 plugin/bbb/lib/vm/AmazonVM.php create mode 100644 plugin/bbb/lib/vm/DigitalOceanVM.php create mode 100644 plugin/bbb/lib/vm/VMInterface.php create mode 100644 plugin/bbb/listing.php create mode 100644 plugin/bbb/plugin.php create mode 100644 plugin/bbb/resources/img/128/bigbluebutton.png create mode 100644 plugin/bbb/resources/img/128/bigbluebutton_na.png create mode 100644 plugin/bbb/resources/img/128/videoconference_flash.png create mode 100644 plugin/bbb/resources/img/128/videoconference_html5.png create mode 100644 plugin/bbb/resources/img/22/bigbluebutton.png create mode 100644 plugin/bbb/resources/img/22/bigbluebutton_na.png create mode 100644 plugin/bbb/resources/img/32/bigbluebutton.png create mode 100644 plugin/bbb/resources/img/32/bigbluebutton_na.png create mode 100644 plugin/bbb/resources/img/64/bigbluebutton.png create mode 100644 plugin/bbb/resources/img/64/bigbluebutton_na.png create mode 100644 plugin/bbb/resources/img/64/videoconference_flash.png create mode 100644 plugin/bbb/resources/img/64/videoconference_html5.png create mode 100644 plugin/bbb/resources/img/svg/bigbluebutton.svg create mode 100644 plugin/bbb/resources/img/svg/bigbluebutton_na.svg create mode 100644 plugin/bbb/resources/img/svg/videoconference_flash.svg create mode 100644 plugin/bbb/resources/img/svg/videoconference_html5.svg create mode 100644 plugin/bbb/resources/utils.js create mode 100644 plugin/bbb/start.php create mode 100644 plugin/bbb/uninstall.php create mode 100644 plugin/bbb/update.php create mode 100644 plugin/bbb/view/admin.tpl create mode 100644 plugin/bbb/view/listing.tpl create mode 100644 plugin/before_login/README.md create mode 100644 plugin/before_login/index.php create mode 100644 plugin/before_login/plugin.php create mode 100644 plugin/before_login/template.tpl create mode 100644 plugin/buycourses/CHANGELOG.md create mode 100644 plugin/buycourses/README.md create mode 100644 plugin/buycourses/admin.php create mode 100644 plugin/buycourses/config.php create mode 100644 plugin/buycourses/database.php create mode 100644 plugin/buycourses/index.php create mode 100644 plugin/buycourses/install.php create mode 100644 plugin/buycourses/lang/brazilian.php create mode 100644 plugin/buycourses/lang/dutch.php create mode 100644 plugin/buycourses/lang/english.php create mode 100644 plugin/buycourses/lang/french.php create mode 100644 plugin/buycourses/lang/spanish.php create mode 100644 plugin/buycourses/plugin.php create mode 100644 plugin/buycourses/resources/css/style.css create mode 100644 plugin/buycourses/resources/img/128/backlogs.png create mode 100644 plugin/buycourses/resources/img/128/buycourses.png create mode 100644 plugin/buycourses/resources/img/128/buysubscriptions.png create mode 100644 plugin/buycourses/resources/img/128/discount.png create mode 100644 plugin/buycourses/resources/img/128/paymentsettings.png create mode 100644 plugin/buycourses/resources/img/128/settings.png create mode 100644 plugin/buycourses/resources/img/128/subscriptionssettings.png create mode 100644 plugin/buycourses/resources/img/32/backlogs.png create mode 100644 plugin/buycourses/resources/img/32/buycourses.png create mode 100644 plugin/buycourses/resources/img/32/buysubscription.png create mode 100644 plugin/buycourses/resources/img/32/buysubscriptions.png create mode 100644 plugin/buycourses/resources/img/32/discount.png create mode 100644 plugin/buycourses/resources/img/32/paymentsettings.png create mode 100644 plugin/buycourses/resources/img/32/settings.png create mode 100644 plugin/buycourses/resources/img/32/subscriptionsettings.png create mode 100644 plugin/buycourses/resources/img/32/subscriptionssettings.png create mode 100644 plugin/buycourses/resources/img/42/backlogs.png create mode 100644 plugin/buycourses/resources/img/42/buycourses.png create mode 100644 plugin/buycourses/resources/img/42/buysubscriptions.png create mode 100644 plugin/buycourses/resources/img/42/discount.png create mode 100644 plugin/buycourses/resources/img/42/paymentsettings.png create mode 100644 plugin/buycourses/resources/img/42/settings.png create mode 100644 plugin/buycourses/resources/img/42/subscriptionssettings.png create mode 100644 plugin/buycourses/resources/img/64/backlogs.png create mode 100644 plugin/buycourses/resources/img/64/buycourses.png create mode 100644 plugin/buycourses/resources/img/64/buysubscriptions.png create mode 100644 plugin/buycourses/resources/img/64/discount.png create mode 100644 plugin/buycourses/resources/img/64/paymentsettings.png create mode 100644 plugin/buycourses/resources/img/64/settings.png create mode 100644 plugin/buycourses/resources/img/64/subscriptionssettings.png create mode 100644 plugin/buycourses/resources/js/commissions.js create mode 100644 plugin/buycourses/resources/js/modals.js create mode 100644 plugin/buycourses/src/buy_course_plugin.class.php create mode 100644 plugin/buycourses/src/buycourses.ajax.php create mode 100644 plugin/buycourses/src/cecabank_cancel.php create mode 100644 plugin/buycourses/src/cecabank_response.php create mode 100644 plugin/buycourses/src/cecabank_success.php create mode 100644 plugin/buycourses/src/configure_coupon.php create mode 100644 plugin/buycourses/src/configure_course.php create mode 100644 plugin/buycourses/src/configure_frequency.php create mode 100644 plugin/buycourses/src/configure_subscription.php create mode 100644 plugin/buycourses/src/coupon_add.php create mode 100644 plugin/buycourses/src/coupons.php create mode 100644 plugin/buycourses/src/course_catalog.php create mode 100644 plugin/buycourses/src/course_panel.php create mode 100644 plugin/buycourses/src/error.php create mode 100644 plugin/buycourses/src/export_report.php create mode 100644 plugin/buycourses/src/export_subscription_report.php create mode 100644 plugin/buycourses/src/expresscheckout.php create mode 100644 plugin/buycourses/src/index.buycourses.php create mode 100644 plugin/buycourses/src/index.php create mode 100644 plugin/buycourses/src/invoice.php create mode 100644 plugin/buycourses/src/list.php create mode 100644 plugin/buycourses/src/list_coupon.php create mode 100644 plugin/buycourses/src/list_service.php create mode 100644 plugin/buycourses/src/list_session.php create mode 100644 plugin/buycourses/src/panel.ajax.php create mode 100644 plugin/buycourses/src/paymentsetup.php create mode 100644 plugin/buycourses/src/payout_panel.php create mode 100644 plugin/buycourses/src/payout_report.php create mode 100644 plugin/buycourses/src/paypal_payout.php create mode 100644 plugin/buycourses/src/paypalfunctions.php create mode 100644 plugin/buycourses/src/process.php create mode 100644 plugin/buycourses/src/process_confirm.php create mode 100644 plugin/buycourses/src/process_subscription_confirm.php create mode 100644 plugin/buycourses/src/sales_report.php create mode 100644 plugin/buycourses/src/service_catalog.php create mode 100644 plugin/buycourses/src/service_error.php create mode 100644 plugin/buycourses/src/service_information.php create mode 100644 plugin/buycourses/src/service_panel.php create mode 100644 plugin/buycourses/src/service_process.php create mode 100644 plugin/buycourses/src/service_process_confirm.php create mode 100644 plugin/buycourses/src/service_sales_report.php create mode 100644 plugin/buycourses/src/service_success.php create mode 100644 plugin/buycourses/src/services_add.php create mode 100644 plugin/buycourses/src/services_edit.php create mode 100644 plugin/buycourses/src/session_catalog.php create mode 100644 plugin/buycourses/src/session_panel.php create mode 100644 plugin/buycourses/src/stripe_cancel.php create mode 100644 plugin/buycourses/src/stripe_response.php create mode 100644 plugin/buycourses/src/stripe_success.php create mode 100644 plugin/buycourses/src/subscription_add.php create mode 100644 plugin/buycourses/src/subscription_course_catalog.php create mode 100644 plugin/buycourses/src/subscription_process.php create mode 100644 plugin/buycourses/src/subscription_sales_report.php create mode 100644 plugin/buycourses/src/subscription_session_catalog.php create mode 100644 plugin/buycourses/src/subscriptions_courses.php create mode 100644 plugin/buycourses/src/subscriptions_sessions.php create mode 100644 plugin/buycourses/src/success.php create mode 100644 plugin/buycourses/src/tpv_error.php create mode 100644 plugin/buycourses/src/tpv_response.php create mode 100644 plugin/buycourses/src/tpv_success.php create mode 100644 plugin/buycourses/uninstall.php create mode 100644 plugin/buycourses/update.php create mode 100644 plugin/buycourses/uploads/services/images/.gitignore create mode 100644 plugin/buycourses/view/catalog.tpl create mode 100644 plugin/buycourses/view/configure_frequency.tpl create mode 100644 plugin/buycourses/view/configure_subscription.tpl create mode 100644 plugin/buycourses/view/coupons.tpl create mode 100644 plugin/buycourses/view/course_panel.tpl create mode 100644 plugin/buycourses/view/export_report.tpl create mode 100644 plugin/buycourses/view/index.tpl create mode 100644 plugin/buycourses/view/list.tpl create mode 100644 plugin/buycourses/view/list_coupon.tpl create mode 100644 plugin/buycourses/view/list_subscription.tpl create mode 100644 plugin/buycourses/view/message_confirm.tpl create mode 100644 plugin/buycourses/view/message_confirm_buyer.tpl create mode 100644 plugin/buycourses/view/message_transfer.tpl create mode 100644 plugin/buycourses/view/paymentsetup.tpl create mode 100644 plugin/buycourses/view/payout_panel.tpl create mode 100644 plugin/buycourses/view/payout_report.tpl create mode 100644 plugin/buycourses/view/paypal_payout.tpl create mode 100644 plugin/buycourses/view/process.tpl create mode 100644 plugin/buycourses/view/process_confirm.tpl create mode 100644 plugin/buycourses/view/sales_report.tpl create mode 100644 plugin/buycourses/view/service_information.tpl create mode 100644 plugin/buycourses/view/service_message_transfer.tpl create mode 100644 plugin/buycourses/view/service_panel.tpl create mode 100644 plugin/buycourses/view/service_process.tpl create mode 100644 plugin/buycourses/view/service_sales_report.tpl create mode 100644 plugin/buycourses/view/session_panel.tpl create mode 100644 plugin/buycourses/view/subscription_add.tpl create mode 100644 plugin/buycourses/view/subscription_catalog.tpl create mode 100644 plugin/buycourses/view/subscription_process.tpl create mode 100644 plugin/buycourses/view/subscription_process_confirm.tpl create mode 100644 plugin/buycourses/view/subscription_sales_report.tpl create mode 100644 plugin/buycourses/view/subscriptions.tpl create mode 100644 plugin/buycourses/view/success.tpl create mode 100644 plugin/card_game/LICENSE.txt create mode 100644 plugin/card_game/README.md create mode 100644 plugin/card_game/card_game.php create mode 100644 plugin/card_game/index.php create mode 100644 plugin/card_game/install.php create mode 100644 plugin/card_game/lang/english.php create mode 100644 plugin/card_game/lang/french.php create mode 100644 plugin/card_game/lang/spanish.php create mode 100644 plugin/card_game/plugin.php create mode 100644 plugin/card_game/resources/ajax.card.php create mode 100644 plugin/card_game/resources/css/animstar.png create mode 100644 plugin/card_game/resources/css/b-back.jpg create mode 100644 plugin/card_game/resources/css/b-back.png create mode 100644 plugin/card_game/resources/css/card-acces-active.svg create mode 100644 plugin/card_game/resources/css/card-acces.svg create mode 100644 plugin/card_game/resources/css/card-back.png create mode 100644 plugin/card_game/resources/css/card1.jpg create mode 100644 plugin/card_game/resources/css/card1.pdn create mode 100644 plugin/card_game/resources/css/cardgame.css create mode 100644 plugin/card_game/resources/css/cardpack.jpg create mode 100644 plugin/card_game/resources/css/cardpack450.jpg create mode 100644 plugin/card_game/resources/css/galerie.png create mode 100644 plugin/card_game/resources/css/img01.jpg create mode 100644 plugin/card_game/resources/css/img02.jpg create mode 100644 plugin/card_game/resources/css/img03.jpg create mode 100644 plugin/card_game/resources/css/img04.jpg create mode 100644 plugin/card_game/resources/css/renderchami.jpg create mode 100644 plugin/card_game/resources/css/scissorsright.gif create mode 100644 plugin/card_game/resources/js/cardgame.js create mode 100644 plugin/card_game/uninstall.php create mode 100644 plugin/check_extra_field_author_company/CheckExtraFieldAuthorsCompanyPlugin.php create mode 100644 plugin/check_extra_field_author_company/README.md create mode 100644 plugin/check_extra_field_author_company/index.php create mode 100644 plugin/check_extra_field_author_company/install.php create mode 100644 plugin/check_extra_field_author_company/plugin.php create mode 100644 plugin/check_extra_field_author_company/uninstall.php create mode 100644 plugin/cleandeletedfiles/README.md create mode 100644 plugin/cleandeletedfiles/admin.php create mode 100644 plugin/cleandeletedfiles/config.php create mode 100644 plugin/cleandeletedfiles/index.php create mode 100644 plugin/cleandeletedfiles/install.php create mode 100644 plugin/cleandeletedfiles/lang/english.php create mode 100644 plugin/cleandeletedfiles/lang/french.php create mode 100644 plugin/cleandeletedfiles/lang/spanish.php create mode 100644 plugin/cleandeletedfiles/plugin.php create mode 100644 plugin/cleandeletedfiles/src/CleanDeletedFilesPlugin.php create mode 100644 plugin/cleandeletedfiles/src/ajax.php create mode 100644 plugin/clockworksms/README.md create mode 100644 plugin/clockworksms/config.php create mode 100644 plugin/clockworksms/index.php create mode 100644 plugin/clockworksms/install.php create mode 100644 plugin/clockworksms/lang/english.php create mode 100644 plugin/clockworksms/lang/spanish.php create mode 100644 plugin/clockworksms/lib/clockworksms.lib.php create mode 100644 plugin/clockworksms/lib/clockworksms_plugin.class.php create mode 100644 plugin/clockworksms/license.txt create mode 100644 plugin/clockworksms/plugin.php create mode 100644 plugin/clockworksms/sms_templates/account_approved_connect.tpl create mode 100644 plugin/clockworksms/sms_templates/account_created_updated_login_password.tpl create mode 100644 plugin/clockworksms/sms_templates/answer_open_question_test_reviewed.tpl create mode 100644 plugin/clockworksms/sms_templates/assignment_been_created_course.tpl create mode 100644 plugin/clockworksms/sms_templates/been_invited_complete_survey_course.tpl create mode 100644 plugin/clockworksms/sms_templates/been_invited_join_group.tpl create mode 100644 plugin/clockworksms/sms_templates/been_subscribed_course.tpl create mode 100644 plugin/clockworksms/sms_templates/certificate_notification.tpl create mode 100644 plugin/clockworksms/sms_templates/check_email_connect_more_info.tpl create mode 100644 plugin/clockworksms/sms_templates/course_opening_request_code_registered.tpl create mode 100644 plugin/clockworksms/sms_templates/course_opening_request_course_code.tpl create mode 100644 plugin/clockworksms/sms_templates/course_opening_request_course_code_approved.tpl create mode 100644 plugin/clockworksms/sms_templates/messages_sent_edited_group_added.tpl create mode 100644 plugin/clockworksms/sms_templates/messages_sent_edited_group_edited.tpl create mode 100644 plugin/clockworksms/sms_templates/new_answer_posted_forum.tpl create mode 100644 plugin/clockworksms/sms_templates/new_course_been_created.tpl create mode 100644 plugin/clockworksms/sms_templates/new_course_suggested_teacher.tpl create mode 100644 plugin/clockworksms/sms_templates/new_file_shared_course_by.tpl create mode 100644 plugin/clockworksms/sms_templates/new_system_announcement_added.tpl create mode 100644 plugin/clockworksms/sms_templates/new_thread_started_forum.tpl create mode 100644 plugin/clockworksms/sms_templates/new_user_pending_approval.tpl create mode 100644 plugin/clockworksms/sms_templates/new_user_subscribed_course.tpl create mode 100644 plugin/clockworksms/sms_templates/password_updated_login_password.tpl create mode 100644 plugin/clockworksms/sms_templates/posted_forum_course.tpl create mode 100644 plugin/clockworksms/sms_templates/received_new_personal_messages.tpl create mode 100644 plugin/clockworksms/sms_templates/reminder_assignment_course_due.tpl create mode 100644 plugin/clockworksms/sms_templates/request_open_course_code_rejected.tpl create mode 100644 plugin/clockworksms/sms_templates/requested_password_change.tpl create mode 100644 plugin/clockworksms/sms_templates/student_answered_test.tpl create mode 100644 plugin/clockworksms/sms_templates/student_answered_test_open_question.tpl create mode 100644 plugin/clockworksms/sms_templates/student_answered_test_voice_question.tpl create mode 100644 plugin/clockworksms/sms_templates/subscribed_session.tpl create mode 100644 plugin/clockworksms/sms_templates/subscribed_session_csv.tpl create mode 100644 plugin/clockworksms/sms_templates/system_announcement_update.tpl create mode 100644 plugin/clockworksms/sms_templates/test_new_system_announcement_added.tpl create mode 100644 plugin/clockworksms/sms_templates/test_system_announcement_update.tpl create mode 100644 plugin/clockworksms/sms_templates/user_answered_inbox_message.tpl create mode 100644 plugin/clockworksms/sms_templates/user_details_modified.tpl create mode 100644 plugin/clockworksms/sms_templates/user_suggested_be_friends.tpl create mode 100644 plugin/clockworksms/sms_templates/user_uploaded_assignment_check.tpl create mode 100644 plugin/clockworksms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl create mode 100644 plugin/clockworksms/sms_templates/user_uploaded_assignment_course.tpl create mode 100644 plugin/clockworksms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl create mode 100644 plugin/clockworksms/sms_templates/welcome_login_password.tpl create mode 100644 plugin/clockworksms/uninstall.php create mode 100644 plugin/clockworksms/vendor/changelog.md create mode 100644 plugin/clockworksms/vendor/clockworksms_api.php create mode 100644 plugin/clockworksms/vendor/exception.php create mode 100644 plugin/clockworksms/vendor/license.txt create mode 100644 plugin/clockworksms/vendor/license.txt~ create mode 100644 plugin/clockworksms/vendor/readme.md create mode 100644 plugin/courseblock/CourseBlockPlugin.php create mode 100644 plugin/courseblock/README.md create mode 100644 plugin/courseblock/config.php create mode 100644 plugin/courseblock/index.php create mode 100644 plugin/courseblock/install.php create mode 100644 plugin/courseblock/lang/english.php create mode 100644 plugin/courseblock/lang/french.php create mode 100644 plugin/courseblock/lang/spanish.php create mode 100644 plugin/courseblock/plugin.php create mode 100644 plugin/coursehomenotify/CourseHomeNotifyPlugin.php create mode 100644 plugin/coursehomenotify/Entity/Notification.php create mode 100644 plugin/coursehomenotify/Entity/NotificationRelUser.php create mode 100644 plugin/coursehomenotify/README.md create mode 100644 plugin/coursehomenotify/configure.php create mode 100644 plugin/coursehomenotify/content.php create mode 100644 plugin/coursehomenotify/install.php create mode 100644 plugin/coursehomenotify/lang/english.php create mode 100644 plugin/coursehomenotify/lang/spanish.php create mode 100644 plugin/coursehomenotify/plugin.php create mode 100644 plugin/coursehomenotify/uninstall.php create mode 100644 plugin/courselegal/CourseLegalPlugin.php create mode 100644 plugin/courselegal/README.md create mode 100644 plugin/courselegal/config.php create mode 100644 plugin/courselegal/index.php create mode 100644 plugin/courselegal/install.php create mode 100644 plugin/courselegal/lang/english.php create mode 100644 plugin/courselegal/lang/french.php create mode 100644 plugin/courselegal/lang/spanish.php create mode 100644 plugin/courselegal/plugin.php create mode 100644 plugin/courselegal/start.php create mode 100644 plugin/courselegal/user_list.php create mode 100644 plugin/createdrupaluser/README.md create mode 100644 plugin/createdrupaluser/config.php create mode 100644 plugin/createdrupaluser/index.php create mode 100644 plugin/createdrupaluser/install.php create mode 100644 plugin/createdrupaluser/lang/english.php create mode 100644 plugin/createdrupaluser/lang/spanish.php create mode 100644 plugin/createdrupaluser/plugin.php create mode 100644 plugin/createdrupaluser/src/CreateDrupalUser.php create mode 100644 plugin/createdrupaluser/src/HookCreateDrupalUser.php create mode 100644 plugin/createdrupaluser/uninstall.php create mode 100644 plugin/customcertificate/README.md create mode 100644 plugin/customcertificate/config.php create mode 100644 plugin/customcertificate/database.php create mode 100644 plugin/customcertificate/index.php create mode 100644 plugin/customcertificate/install.php create mode 100644 plugin/customcertificate/lang/english.php create mode 100644 plugin/customcertificate/lang/spanish.php create mode 100644 plugin/customcertificate/plugin.php create mode 100644 plugin/customcertificate/resources/css/certificate.css create mode 100644 plugin/customcertificate/resources/css/form.css create mode 100644 plugin/customcertificate/resources/img/22/customcertificate.png create mode 100644 plugin/customcertificate/resources/img/64/customcertificate.png create mode 100644 plugin/customcertificate/resources/img/64/customcertificate_na.png create mode 100644 plugin/customcertificate/resources/js/certificate.js create mode 100644 plugin/customcertificate/src/CustomCertificatePlugin.php create mode 100644 plugin/customcertificate/src/customcertificate.ajax.php create mode 100644 plugin/customcertificate/src/export_pdf_all_in_one.php create mode 100644 plugin/customcertificate/src/index.php create mode 100644 plugin/customcertificate/src/print_certificate.php create mode 100644 plugin/customcertificate/start.php create mode 100644 plugin/customcertificate/uninstall.php create mode 100644 plugin/customcertificate/update.php create mode 100644 plugin/customfooter/index.php create mode 100644 plugin/customfooter/install.php create mode 100644 plugin/customfooter/lang/en/tool_delivery.php create mode 100644 plugin/customfooter/lang/fr/tool_delivery.php create mode 100644 plugin/customfooter/lib/customfooter_plugin.class.php create mode 100644 plugin/customfooter/plugin.php create mode 100644 plugin/dashboard/block_course/block_course.class.php create mode 100644 plugin/dashboard/block_course/block_course.info create mode 100644 plugin/dashboard/block_course/index.html create mode 100644 plugin/dashboard/block_daily/block_daily.class.php create mode 100644 plugin/dashboard/block_daily/block_daily.info create mode 100644 plugin/dashboard/block_daily/index.html create mode 100644 plugin/dashboard/block_evaluation_graph/block_evaluation_graph.class.php create mode 100644 plugin/dashboard/block_evaluation_graph/block_evaluation_graph.info create mode 100644 plugin/dashboard/block_global_info/block_global_info.class.php create mode 100644 plugin/dashboard/block_global_info/block_global_info.info create mode 100644 plugin/dashboard/block_global_info/index.html create mode 100644 plugin/dashboard/block_session/block_session.class.php create mode 100644 plugin/dashboard/block_session/block_session.info create mode 100644 plugin/dashboard/block_session/index.html create mode 100644 plugin/dashboard/block_student/block_student.class.php create mode 100644 plugin/dashboard/block_student/block_student.info create mode 100644 plugin/dashboard/block_student/index.html create mode 100644 plugin/dashboard/block_student_graph/block_student_graph.class.php create mode 100644 plugin/dashboard/block_student_graph/block_student_graph.info create mode 100644 plugin/dashboard/block_teacher/block_teacher.class.php create mode 100644 plugin/dashboard/block_teacher/block_teacher.info create mode 100644 plugin/dashboard/block_teacher/index.html create mode 100644 plugin/dashboard/block_teacher_graph/block_teacher_graph.class.php create mode 100644 plugin/dashboard/block_teacher_graph/block_teacher_graph.info create mode 100644 plugin/dashboard/css/default.css create mode 100644 plugin/dashboard/index.html create mode 100644 plugin/date/README.md create mode 100644 plugin/date/index.act.php create mode 100644 plugin/date/index.dsp.php create mode 100644 plugin/date/index.php create mode 100644 plugin/date/plugin.php create mode 100644 plugin/date/start.php create mode 100644 plugin/dictionary/DictionaryPlugin.php create mode 100644 plugin/dictionary/index.php create mode 100644 plugin/dictionary/install.php create mode 100644 plugin/dictionary/lang/english.php create mode 100644 plugin/dictionary/plugin.php create mode 100644 plugin/dictionary/terms.php create mode 100644 plugin/dictionary/view/terms.html.twig create mode 100644 plugin/embedregistry/EmbedRegistryPlugin.php create mode 100644 plugin/embedregistry/Entity/Embed.php create mode 100644 plugin/embedregistry/README.md create mode 100644 plugin/embedregistry/install.php create mode 100644 plugin/embedregistry/lang/english.php create mode 100644 plugin/embedregistry/lang/french.php create mode 100644 plugin/embedregistry/lang/spanish.php create mode 100644 plugin/embedregistry/plugin.php create mode 100644 plugin/embedregistry/start.php create mode 100644 plugin/embedregistry/uninstall.php create mode 100644 plugin/embedregistry/view.php create mode 100644 plugin/embedregistry/view/start.tpl create mode 100644 plugin/exercise_signature/README.md create mode 100644 plugin/exercise_signature/index.php create mode 100644 plugin/exercise_signature/install.php create mode 100644 plugin/exercise_signature/lang/english.php create mode 100644 plugin/exercise_signature/lang/french.php create mode 100644 plugin/exercise_signature/lang/spanish.php create mode 100644 plugin/exercise_signature/lib/ExerciseSignature.php create mode 100644 plugin/exercise_signature/plugin.php create mode 100644 plugin/exercise_signature/uninstall.php create mode 100644 plugin/exercisefocused/admin.php create mode 100644 plugin/exercisefocused/index.php create mode 100644 plugin/exercisefocused/install.php create mode 100644 plugin/exercisefocused/lang/english.php create mode 100644 plugin/exercisefocused/lang/spanish.php create mode 100644 plugin/exercisefocused/pages/detail.php create mode 100644 plugin/exercisefocused/pages/export.php create mode 100644 plugin/exercisefocused/pages/log.php create mode 100644 plugin/exercisefocused/pages/reporting.php create mode 100644 plugin/exercisefocused/plugin.php create mode 100644 plugin/exercisefocused/src/Controller/AdminController.php create mode 100644 plugin/exercisefocused/src/Controller/BaseController.php create mode 100644 plugin/exercisefocused/src/Controller/DetailController.php create mode 100644 plugin/exercisefocused/src/Controller/LogController.php create mode 100644 plugin/exercisefocused/src/Controller/ReportingController.php create mode 100644 plugin/exercisefocused/src/Entity/Log.php create mode 100644 plugin/exercisefocused/src/ExerciseFocusedPlugin.php create mode 100644 plugin/exercisefocused/src/Repository/LogRepository.php create mode 100644 plugin/exercisefocused/src/Traits/DetailControllerTrait.php create mode 100644 plugin/exercisefocused/src/Traits/ReportingFilterTrait.php create mode 100644 plugin/exercisefocused/templates/block.html.twig create mode 100644 plugin/exercisefocused/templates/script.html.twig create mode 100644 plugin/exercisefocused/uninstall.php create mode 100644 plugin/exercisemonitoring/admin.php create mode 100644 plugin/exercisemonitoring/assets/images/idcard.png create mode 100644 plugin/exercisemonitoring/assets/images/user.png create mode 100644 plugin/exercisemonitoring/cron/cleanup.php create mode 100644 plugin/exercisemonitoring/index.php create mode 100644 plugin/exercisemonitoring/install.php create mode 100644 plugin/exercisemonitoring/lang/english.php create mode 100644 plugin/exercisemonitoring/lang/spanish.php create mode 100644 plugin/exercisemonitoring/pages/detail.php create mode 100644 plugin/exercisemonitoring/pages/exercise_submit.ajax.php create mode 100644 plugin/exercisemonitoring/pages/start.ajax.php create mode 100644 plugin/exercisemonitoring/plugin.php create mode 100644 plugin/exercisemonitoring/src/Controller/DetailController.php create mode 100644 plugin/exercisemonitoring/src/Controller/ExerciseSubmitController.php create mode 100644 plugin/exercisemonitoring/src/Controller/StartController.php create mode 100644 plugin/exercisemonitoring/src/Entity/Log.php create mode 100644 plugin/exercisemonitoring/src/ExerciseMonitoringPlugin.php create mode 100644 plugin/exercisemonitoring/src/Repository/LogRepository.php create mode 100644 plugin/exercisemonitoring/templates/exercise_submit.html.twig create mode 100644 plugin/exercisemonitoring/templates/modal.html.twig create mode 100644 plugin/exercisemonitoring/uninstall.php create mode 100644 plugin/ext_auth_chamilo_logout_button_behaviour/README.md create mode 100644 plugin/ext_auth_chamilo_logout_button_behaviour/index.php create mode 100644 plugin/ext_auth_chamilo_logout_button_behaviour/plugin.php create mode 100644 plugin/ext_auth_chamilo_logout_button_behaviour/template.tpl create mode 100644 plugin/externalnotificationconnect/README.md create mode 100644 plugin/externalnotificationconnect/install.php create mode 100644 plugin/externalnotificationconnect/lang/english.php create mode 100644 plugin/externalnotificationconnect/lang/french.php create mode 100644 plugin/externalnotificationconnect/lang/spanish.php create mode 100644 plugin/externalnotificationconnect/plugin.php create mode 100644 plugin/externalnotificationconnect/src/Entity/AccessToken.php create mode 100644 plugin/externalnotificationconnect/src/ExternalNotificationConnectPlugin.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectLearningPathCreatedHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemAddedHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemDeletedHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemEditedHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemVisibilityHookObserver.php create mode 100644 plugin/externalnotificationconnect/src/Traits/RequestTrait.php create mode 100644 plugin/externalnotificationconnect/uninstall.php create mode 100644 plugin/extramenufromwebservice/LICENSE.txt create mode 100644 plugin/extramenufromwebservice/README.md create mode 100644 plugin/extramenufromwebservice/config.php create mode 100644 plugin/extramenufromwebservice/index.php create mode 100644 plugin/extramenufromwebservice/install.php create mode 100644 plugin/extramenufromwebservice/lang/english.php create mode 100644 plugin/extramenufromwebservice/lang/french.php create mode 100644 plugin/extramenufromwebservice/lang/spanish.php create mode 100644 plugin/extramenufromwebservice/lib/extramenufromwebservice_plugin.class.php create mode 100644 plugin/extramenufromwebservice/plugin.php create mode 100644 plugin/extramenufromwebservice/resources/css/extramenufromwebservice.css create mode 100644 plugin/extramenufromwebservice/resources/js/extramenufromwebservice.js create mode 100644 plugin/extramenufromwebservice/uninstall.php create mode 100644 plugin/follow_buttons/README.md create mode 100644 plugin/follow_buttons/index.php create mode 100644 plugin/follow_buttons/plugin.php create mode 100644 plugin/follow_buttons/template.tpl create mode 100644 plugin/formLogin_hide_unhide/README.md create mode 100644 plugin/formLogin_hide_unhide/css.css create mode 100644 plugin/formLogin_hide_unhide/index.php create mode 100644 plugin/formLogin_hide_unhide/plugin.php create mode 100644 plugin/formLogin_hide_unhide/template.tpl create mode 100644 plugin/google_maps/README.md create mode 100644 plugin/google_maps/config.php create mode 100644 plugin/google_maps/index.php create mode 100644 plugin/google_maps/install.php create mode 100644 plugin/google_maps/lang/english.php create mode 100644 plugin/google_maps/lang/french.php create mode 100644 plugin/google_maps/lang/spanish.php create mode 100644 plugin/google_maps/plugin.php create mode 100644 plugin/google_maps/src/GoogleMapsPlugin.php create mode 100644 plugin/google_maps/src/map_coordinates.php create mode 100644 plugin/google_maps/uninstall.php create mode 100644 plugin/google_maps/view/map_coordinates.tpl create mode 100644 plugin/google_meet/README.md create mode 100644 plugin/google_meet/config.php create mode 100644 plugin/google_meet/install.php create mode 100644 plugin/google_meet/lang/english.php create mode 100644 plugin/google_meet/lang/french.php create mode 100644 plugin/google_meet/lang/spanish.php create mode 100644 plugin/google_meet/meets.php create mode 100644 plugin/google_meet/plugin.php create mode 100644 plugin/google_meet/resources/css/style.css create mode 100644 plugin/google_meet/resources/img/22/google_meet.png create mode 100644 plugin/google_meet/resources/img/22/google_meet_na.png create mode 100644 plugin/google_meet/resources/img/32/google_meet.png create mode 100644 plugin/google_meet/resources/img/32/google_meet_na.png create mode 100644 plugin/google_meet/resources/img/64/google_meet.png create mode 100644 plugin/google_meet/resources/img/64/google_meet_na.png create mode 100644 plugin/google_meet/resources/img/svg/googlemeet.svg create mode 100644 plugin/google_meet/resources/img/svg/meet_room.svg create mode 100644 plugin/google_meet/src/google_meet_plugin.class.php create mode 100644 plugin/google_meet/start.php create mode 100644 plugin/google_meet/uninstall.php create mode 100644 plugin/google_meet/view/home.tpl create mode 100644 plugin/google_meet/view/meets.tpl create mode 100644 plugin/grading_electronic/README.md create mode 100644 plugin/grading_electronic/generate.php create mode 100644 plugin/grading_electronic/index.php create mode 100644 plugin/grading_electronic/install.php create mode 100644 plugin/grading_electronic/lang/english.php create mode 100644 plugin/grading_electronic/plugin.php create mode 100644 plugin/grading_electronic/src/GradingElectronicPlugin.php create mode 100644 plugin/grading_electronic/uninstall.php create mode 100644 plugin/grading_electronic/view/grading.html.twig create mode 100644 plugin/h5p/README.md create mode 100644 plugin/h5p/cache-h5p/.eslintrc.json create mode 100644 plugin/h5p/cache-h5p/.github/workflows/cypress.yml create mode 100644 plugin/h5p/cache-h5p/.gitignore create mode 100644 plugin/h5p/cache-h5p/CHANGELOG.md create mode 100644 plugin/h5p/cache-h5p/LICENSE create mode 100644 plugin/h5p/cache-h5p/README.md create mode 100644 plugin/h5p/cache-h5p/cypress.json create mode 100644 plugin/h5p/cache-h5p/cypress/integration/multiple.spec.js create mode 100644 plugin/h5p/cache-h5p/cypress/integration/single.spec.js create mode 100644 plugin/h5p/cache-h5p/cypress/plugins/index.js create mode 100644 plugin/h5p/cache-h5p/cypress/support/commands.js create mode 100644 plugin/h5p/cache-h5p/cypress/support/index.js create mode 100644 plugin/h5p/cache-h5p/dist/fonts/h5p-core-23.eot create mode 100644 plugin/h5p/cache-h5p/dist/fonts/h5p-core-23.svg create mode 100644 plugin/h5p/cache-h5p/dist/fonts/h5p-core-23.ttf create mode 100644 plugin/h5p/cache-h5p/dist/fonts/h5p-core-23.woff create mode 100644 plugin/h5p/cache-h5p/dist/frame.bundle.js create mode 100644 plugin/h5p/cache-h5p/dist/main.bundle.js create mode 100644 plugin/h5p/cache-h5p/dist/styles/h5p-admin.css create mode 100644 plugin/h5p/cache-h5p/dist/styles/h5p-confirmation-dialog.css create mode 100644 plugin/h5p/cache-h5p/dist/styles/h5p-core-button.css create mode 100644 plugin/h5p/cache-h5p/dist/styles/h5p.css create mode 100644 plugin/h5p/cache-h5p/launch/dialog-card.html create mode 100644 plugin/h5p/cache-h5p/launch/drag-the-words.html create mode 100644 plugin/h5p/cache-h5p/launch/guesstheanswer.html create mode 100644 plugin/h5p/cache-h5p/launch/memory.html create mode 100644 plugin/h5p/cache-h5p/launch/mots-caches.html create mode 100644 plugin/h5p/cache-h5p/launch/multiple.html create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/css/drop-theme-arrows-bounce.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/js/drop.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hans.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hant.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/scripts/audio.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/styles/audio.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/dist/h5p-dialogcards.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/dist/h5p-dialogcards.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/hr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/uk.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/language/zh.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Dialogcards-1.8/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-help-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-icon.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-message-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-progress-circle.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-progressbar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-score-bar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-simple-rounded-button.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-slider.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-speech-bubble.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-tip.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/css/joubel-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/fonts/joubel.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/fonts/joubel.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/fonts/joubel.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/fonts/joubel.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-help-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-message-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-progress-circle.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-progressbar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-score-bar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-simple-rounded-button.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-slider.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-speech-bubble.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-throbber.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-tip.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/js/joubel-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.JoubelUI-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Transition-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Transition-1.0/transition.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.ShowWhen-1.0/h5p-show-when.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.ShowWhen-1.0/h5p-show-when.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.ShowWhen-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.VerticalTabs-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.VerticalTabs-1.3/styles/css/vertical-tabs.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/H5PEditor.VerticalTabs-1.3/vertical-tabs.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Tether-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Tether-1.0/scripts/tether.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/Tether-1.0/styles/tether.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/content/images/dialogcard.jpg create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/flowplayer-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/flowplayer-1.0/scripts/flowplayer-3.2.12.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dialogcard/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Drop-1.0/css/drop-theme-arrows-bounce.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Drop-1.0/js/drop.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Drop-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/dist/h5p-drag-text.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/dist/h5p-drag-text.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/uk.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/language/zh-tw.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/presave.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.DragText-1.8/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-help-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-icon.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-message-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-progress-circle.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-progressbar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-score-bar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-simple-rounded-button.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-slider.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-speech-bubble.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-tip.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/css/joubel-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/fonts/joubel.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/fonts/joubel.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/fonts/joubel.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/fonts/joubel.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-help-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-message-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-progress-circle.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-progressbar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-score-bar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-simple-rounded-button.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-slider.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-speech-bubble.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-throbber.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-tip.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/js/joubel-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.JoubelUI-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/images/minus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/images/plus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/scripts/explainer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/scripts/question.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/scripts/score-points.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/styles/explainer.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Question-1.4/styles/question.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Transition-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5P.Transition-1.0/transition.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/h5p-editor-range-list.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/h5p-editor-range-list.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/en.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.RangeList-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.TableList-1.0/h5p-editor-table-list.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.TableList-1.0/h5p-editor-table-list.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/H5PEditor.TableList-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Tether-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Tether-1.0/scripts/tether.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/Tether-1.0/styles/tether.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/apply-when-upgrading.diff create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/h5p-jquery-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/h5p-jquery-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/animated-overlay.gif create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-icons_222222_256x240.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-icons_2e83ff_256x240.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-icons_454545_256x240.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-icons_888888_256x240.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/images/ui-icons_cd0a0a_256x240.png create mode 100644 plugin/h5p/cache-h5p/launch/source-dragthewords/jQuery.ui-1.10/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/guess-the-answer.css create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/guess-the-answer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/language/zh-hans.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.GuessTheAnswer-1.4/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/image.css create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/image.js create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/zh-hans.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/language/zh-hant.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/placeholder.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/H5P.Image-1.1/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/content/images/file-5ef786ac3f86d.jpg create mode 100644 plugin/h5p/cache-h5p/launch/source-guesstheanswer/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/source-h.html create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Drop-1.0/css/drop-theme-arrows-bounce.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Drop-1.0/js/drop.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Drop-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-help-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-icon.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-message-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-progress-circle.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-progressbar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-score-bar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-simple-rounded-button.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-slider.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-speech-bubble.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-tip.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/css/joubel-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/fonts/joubel.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/fonts/joubel.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/fonts/joubel.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/fonts/joubel.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-help-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-message-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-progress-circle.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-progressbar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-score-bar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-simple-rounded-button.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-slider.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-speech-bubble.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-throbber.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-tip.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/js/joubel-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.JoubelUI-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/uk.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/language/zh.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/presave.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/scripts/keyboard-nav.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/scripts/mark-the-words.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/scripts/word.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/scripts/xAPI-generator.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/styles/mark-the-words.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.MarkTheWords-1.9/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/images/minus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/images/plus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/scripts/explainer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/scripts/question.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/scripts/score-points.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/styles/explainer.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Question-1.4/styles/question.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Transition-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5P.Transition-1.0/transition.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/h5p-editor-range-list.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/h5p-editor-range-list.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/bg.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/en.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.RangeList-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.TableList-1.0/h5p-editor-table-list.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.TableList-1.0/h5p-editor-table-list.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/H5PEditor.TableList-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Tether-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Tether-1.0/scripts/tether.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/Tether-1.0/styles/tether.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-markthewords/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/card.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/counter.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/es-mx.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/pt-br.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/sl.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/uk.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/zh-hans.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/zh-hant.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/language/zh-tw.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/memory-game.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/memory-game.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/popup.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/timer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.MemoryGame-1.3/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.Timer-0.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5P.Timer-0.4/scripts/timer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/dist/h5p-editor-audio-recorder.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/en.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/sma.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/sme.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/smj.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/language/zh.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.AudioRecorder-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.ColorSelector-1.2/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.ColorSelector-1.2/scripts/color-selector.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.ColorSelector-1.2/scripts/spectrum.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.ColorSelector-1.2/styles/color-selector.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.ColorSelector-1.2/styles/spectrum.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.VerticalTabs-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.VerticalTabs-1.3/styles/css/vertical-tabs.css create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/H5PEditor.VerticalTabs-1.3/vertical-tabs.js create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/content/images/image-5e3b1b2ec709c.jpg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/content/images/image-5e3b1b45cadc3.jpg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/content/images/image-5e3b1b52d7174.jpg create mode 100644 plugin/h5p/cache-h5p/launch/source-memory/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Drop-1.0/css/drop-theme-arrows-bounce.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Drop-1.0/js/drop.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Drop-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/scripts/h5p-find-the-words-counter.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/scripts/h5p-find-the-words-timer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/scripts/h5p-find-the-words-vocabulary.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/scripts/h5p-find-the-words-word-grid.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/scripts/h5p-find-the-words.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FindTheWords-1.4/styles/h5p-find-the-words.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-help-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-icon.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-message-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-progress-circle.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-progressbar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-score-bar.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-simple-rounded-button.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-slider.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-speech-bubble.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-tip.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/css/joubel-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/fonts/joubel.eot create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/fonts/joubel.svg create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/fonts/joubel.ttf create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/fonts/joubel.woff create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-help-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-message-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-progress-circle.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-progressbar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-score-bar.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-simple-rounded-button.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-slider.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-speech-bubble.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-throbber.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-tip.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/js/joubel-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.JoubelUI-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.Timer-0.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.Timer-0.4/scripts/timer.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.Transition-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/H5P.Transition-1.0/transition.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Tether-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Tether-1.0/scripts/tether.min.js create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/Tether-1.0/styles/tether.min.css create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/source-wordsmatch/h5p.json create mode 100644 plugin/h5p/cache-h5p/launch/test-mark-the-words.html create mode 100644 plugin/h5p/cache-h5p/launch/wordsmatch.html create mode 100644 plugin/h5p/cache-h5p/launch/workspace.html create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Drop-1.0/css/drop-theme-arrows-bounce.min.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Drop-1.0/js/drop.min.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Drop-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/FontAwesome.otf create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/fontawesome-webfont.eot create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/fontawesome-webfont.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/fontawesome-webfont.ttf create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/fontawesome-webfont.woff create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/fontawesome-webfont.woff2 create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/h5p-font-awesome.min.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/FontAwesome-4.5/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/fonts/h5p.eot create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/fonts/h5p.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/fonts/h5p.ttf create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/fonts/h5p.woff create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.FontIcons-1.0/styles/h5p-font-icons.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-help-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-icon.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-message-dialog.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-progress-circle.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-progressbar.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-score-bar.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-simple-rounded-button.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-slider.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-speech-bubble.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-tip.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/css/joubel-ui.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/fonts/joubel.eot create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/fonts/joubel.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/fonts/joubel.ttf create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/fonts/joubel.woff create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-help-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-message-dialog.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-progress-circle.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-progressbar.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-score-bar.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-simple-rounded-button.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-slider.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-speech-bubble.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-throbber.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-tip.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/js/joubel-ui.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.JoubelUI-1.3/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/images/minus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/images/plus-one.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/scripts/explainer.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/scripts/question.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/scripts/score-points.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/styles/explainer.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Question-1.4/styles/question.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Transition-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.Transition-1.0/transition.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/icon.svg create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/af.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ar.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/bs.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ca.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/cs.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/da.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/de.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/el.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/es.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/et.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/eu.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/fi.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/fr.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/he.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/hu.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/it.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ja.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ko.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/nb.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/nl.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/nn.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/pl.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/pt.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ro.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/ru.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/sr.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/sv.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/tr.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/uk.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/vi.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/zh-hans.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/language/zh-hant.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/presave.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/scripts/h5p-true-false-answer-group.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/scripts/h5p-true-false-answer.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/scripts/h5p-true-false.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/semantics.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/styles/h5p-true-false.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P.TrueFalse-1.6/upgrades.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.RadioGroup-1.1/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.RadioGroup-1.1/radio-group.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.RadioGroup-1.1/radio-group.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.ShowWhen-1.0/h5p-show-when.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.ShowWhen-1.0/h5p-show-when.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5PEditor.ShowWhen-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/H5P_CONTENT_HERE.txt create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Tether-1.0/library.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Tether-1.0/scripts/tether.min.js create mode 100644 plugin/h5p/cache-h5p/launch/workspace/Tether-1.0/styles/tether.min.css create mode 100644 plugin/h5p/cache-h5p/launch/workspace/content/content.json create mode 100644 plugin/h5p/cache-h5p/launch/workspace/h5p.json create mode 100644 plugin/h5p/cache-h5p/package.json create mode 100644 plugin/h5p/cache-h5p/src/js/frame.js create mode 100644 plugin/h5p/cache-h5p/src/js/h5p-integration.js create mode 100644 plugin/h5p/cache-h5p/src/js/h5p-standalone.class.js create mode 100644 plugin/h5p/cache-h5p/src/js/index.js create mode 100644 plugin/h5p/cache-h5p/webpack.common.js create mode 100644 plugin/h5p/cache-h5p/webpack.config.js create mode 100644 plugin/h5p/cache-h5p/webpack.dev.js create mode 100644 plugin/h5p/cache-h5p/webpack.prod.js create mode 100644 plugin/h5p/h5p_plugin.class.php create mode 100644 plugin/h5p/inc/action.switch.php create mode 100644 plugin/h5p/inc/edit.form.php create mode 100644 plugin/h5p/inc/translate.php create mode 100644 plugin/h5p/index.php create mode 100644 plugin/h5p/install.php create mode 100644 plugin/h5p/lang/english.php create mode 100644 plugin/h5p/lang/french.php create mode 100644 plugin/h5p/lang/spanish.php create mode 100644 plugin/h5p/list.php create mode 100644 plugin/h5p/node_process.php create mode 100644 plugin/h5p/plugin.php create mode 100644 plugin/h5p/resources/ajax/getnodes.php create mode 100644 plugin/h5p/resources/css/H5P.css create mode 100644 plugin/h5p/resources/css/demo.css create mode 100644 plugin/h5p/resources/css/window-h5p.css create mode 100644 plugin/h5p/resources/img/add.png create mode 100644 plugin/h5p/resources/img/delete.png create mode 100644 plugin/h5p/resources/img/dialogcard.jpg create mode 100644 plugin/h5p/resources/img/dialogcard.png create mode 100644 plugin/h5p/resources/img/dragthewords.png create mode 100644 plugin/h5p/resources/img/edit.png create mode 100644 plugin/h5p/resources/img/guesstheanswer.png create mode 100644 plugin/h5p/resources/img/h5pchami.png create mode 100644 plugin/h5p/resources/img/loadtable.gif create mode 100644 plugin/h5p/resources/img/markthewords.png create mode 100644 plugin/h5p/resources/img/memory.png create mode 100644 plugin/h5p/resources/img/wordsmatch.png create mode 100644 plugin/h5p/resources/js/h5p_extras.js create mode 100644 plugin/h5p/resources/js/interface.js create mode 100644 plugin/h5p/resources/js/jquery.dataTables.min.js create mode 100644 plugin/h5p/resources/js/pell.js create mode 100644 plugin/h5p/resources/js/pell.min.css create mode 100644 plugin/h5p/resources/js/pell.min.js create mode 100644 plugin/h5p/uninstall.php create mode 100644 plugin/h5p/update.php create mode 100644 plugin/h5p/view/list.tpl create mode 100644 plugin/h5p/view/update.tpl create mode 100644 plugin/h5p/view/view.tpl create mode 100644 plugin/h5pimport/Entity/H5pImport.php create mode 100644 plugin/h5pimport/Entity/H5pImportLibrary.php create mode 100644 plugin/h5pimport/Entity/H5pImportResults.php create mode 100644 plugin/h5pimport/H5pImportPlugin.php create mode 100644 plugin/h5pimport/LICENSE.txt create mode 100644 plugin/h5pimport/README.md create mode 100644 plugin/h5pimport/config.php create mode 100644 plugin/h5pimport/install.php create mode 100644 plugin/h5pimport/lang/english.php create mode 100644 plugin/h5pimport/lang/french.php create mode 100644 plugin/h5pimport/lang/spanish.php create mode 100644 plugin/h5pimport/plugin.php create mode 100644 plugin/h5pimport/src/H5pImplementation.php create mode 100644 plugin/h5pimport/src/H5pPackageImporter.php create mode 100644 plugin/h5pimport/src/H5pPackageTools.php create mode 100644 plugin/h5pimport/src/Hook/H5pImportCreateCourseHookObserver.php create mode 100644 plugin/h5pimport/src/ZipPackageImporter.php create mode 100644 plugin/h5pimport/src/ajax.php create mode 100644 plugin/h5pimport/start.php create mode 100644 plugin/h5pimport/uninstall.php create mode 100644 plugin/h5pimport/view.php create mode 100644 plugin/h5pimport/view/index.tpl create mode 100644 plugin/hello_world/README.md create mode 100644 plugin/hello_world/index.php create mode 100644 plugin/hello_world/install.php create mode 100644 plugin/hello_world/lang/english.php create mode 100644 plugin/hello_world/lang/french.php create mode 100644 plugin/hello_world/lang/spanish.php create mode 100644 plugin/hello_world/plugin.php create mode 100644 plugin/hello_world/uninstall.php create mode 100644 plugin/ims_lti/.htaccess create mode 100644 plugin/ims_lti/Entity/ImsLtiTool.php create mode 100644 plugin/ims_lti/Entity/LineItem.php create mode 100644 plugin/ims_lti/Entity/Platform.php create mode 100644 plugin/ims_lti/Entity/Token.php create mode 100644 plugin/ims_lti/ImsLtiPlugin.php create mode 100644 plugin/ims_lti/README.md create mode 100644 plugin/ims_lti/admin.php create mode 100644 plugin/ims_lti/ags2.php create mode 100644 plugin/ims_lti/assets/style.css create mode 100644 plugin/ims_lti/auth.php create mode 100644 plugin/ims_lti/configure.php create mode 100644 plugin/ims_lti/create.php create mode 100644 plugin/ims_lti/delete.php create mode 100644 plugin/ims_lti/edit.php create mode 100644 plugin/ims_lti/form.php create mode 100644 plugin/ims_lti/gradebook/OutcomeForm.php create mode 100644 plugin/ims_lti/gradebook/add_eval.php create mode 100644 plugin/ims_lti/install.php create mode 100644 plugin/ims_lti/item_return.php create mode 100644 plugin/ims_lti/item_return2.php create mode 100644 plugin/ims_lti/jwks.php create mode 100644 plugin/ims_lti/lang/english.php create mode 100644 plugin/ims_lti/lang/french.php create mode 100644 plugin/ims_lti/lang/spanish.php create mode 100644 plugin/ims_lti/login.php create mode 100644 plugin/ims_lti/multiply.php create mode 100644 plugin/ims_lti/multiply_session.php create mode 100644 plugin/ims_lti/nrps2.php create mode 100644 plugin/ims_lti/outcome_service.php create mode 100644 plugin/ims_lti/platform.php create mode 100644 plugin/ims_lti/plugin.php create mode 100644 plugin/ims_lti/session.php create mode 100644 plugin/ims_lti/src/ContentItem/LtiContentItemType.php create mode 100644 plugin/ims_lti/src/ContentItem/LtiResourceLink.php create mode 100644 plugin/ims_lti/src/Form/FrmAdd.php create mode 100644 plugin/ims_lti/src/Form/FrmEdit.php create mode 100644 plugin/ims_lti/src/ImsLti.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceDeleteRequest.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceDeleteResponse.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceReadRequest.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceReadResponse.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceReplaceRequest.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceReplaceResponse.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceRequest.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceRequestFactory.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceResponse.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceResponseFactory.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceResponseStatus.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceUnsupportedRequest.php create mode 100644 plugin/ims_lti/src/ImsLtiServiceUnsupportedResponse.php create mode 100644 plugin/ims_lti/src/LtiAuthException.php create mode 100644 plugin/ims_lti/src/Request/LtiTokenRequest.php create mode 100644 plugin/ims_lti/src/Service/LtiAdvantageService.php create mode 100644 plugin/ims_lti/src/Service/LtiAssignmentGradesService.php create mode 100644 plugin/ims_lti/src/Service/LtiNamesRoleProvisioningService.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiAdvantageServiceResource.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiContextMembershipResource.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiLineItemResource.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiLineItemsResource.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiResultsResource.php create mode 100644 plugin/ims_lti/src/Service/Resource/LtiScoresResource.php create mode 100644 plugin/ims_lti/start.php create mode 100644 plugin/ims_lti/token.php create mode 100644 plugin/ims_lti/tool_settings.php create mode 100644 plugin/ims_lti/uninstall.php create mode 100644 plugin/ims_lti/vendor/oauth1/.gitignore create mode 100644 plugin/ims_lti/vendor/oauth1/CHANGELOG.md create mode 100644 plugin/ims_lti/vendor/oauth1/LICENSE.txt create mode 100644 plugin/ims_lti/vendor/oauth1/README.md create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthConsumer.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthDataStore.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthException.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthRequest.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthServer.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthSignatureMethod.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthSignatureMethod_HMAC_SHA1.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthSignatureMethod_PLAINTEXT.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthSignatureMethod_RSA_SHA1.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthTests.xml create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthToken.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuthUtil.php create mode 100644 plugin/ims_lti/vendor/oauth1/code/OAuth_TestServer.php create mode 100644 plugin/ims_lti/vendor/oauth1/composer.json create mode 100644 plugin/ims_lti/vendor/oauth1/composer.lock create mode 100644 plugin/ims_lti/vendor/oauth1/doc/design.md create mode 100644 plugin/ims_lti/vendor/oauth1/example/SimpleOAuthDataStore.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/access_token.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/client.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/common.inc.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/echo_api.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/index.php create mode 100644 plugin/ims_lti/vendor/oauth1/example/request_token.php create mode 100644 plugin/ims_lti/vendor/oauth1/init.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthBaseStringRequest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthDataStore.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/Mock_OAuthSignatureMethod_RSA_SHA1.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthConsumerTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthRequestTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthServerTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodHmacSha1Test.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodPlaintextTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthSignatureMethodRsaSha1Test.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthTokenTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/OAuthUtilTest.php create mode 100644 plugin/ims_lti/vendor/oauth1/tests/common.php create mode 100644 plugin/ims_lti/view/add.tpl create mode 100644 plugin/ims_lti/view/admin.tpl create mode 100644 plugin/ims_lti/view/start.tpl create mode 100644 plugin/index.html create mode 100644 plugin/jcapture/JCaptureApplet.html create mode 100644 plugin/jcapture/README.md create mode 100644 plugin/jcapture/action.php create mode 100644 plugin/jcapture/applet.php create mode 100644 plugin/jcapture/camera.png create mode 100644 plugin/jcapture/jcapture.jnlp create mode 100644 plugin/jcapture/lib/apache-mime4j-0.6.jar create mode 100644 plugin/jcapture/lib/commons-codec-1.3.jar create mode 100644 plugin/jcapture/lib/commons-logging-1.1.1.jar create mode 100644 plugin/jcapture/lib/httpclient-4.0.1.jar create mode 100644 plugin/jcapture/lib/httpcore-4.0.1.jar create mode 100644 plugin/jcapture/lib/httpmime-4.0.1.jar create mode 100644 plugin/jcapture/lib/jcapture.jar create mode 100644 plugin/jcapture/lib/transform-3.0.2.jar create mode 100644 plugin/jcapture/license.txt create mode 100644 plugin/jcapture/plugin.php create mode 100644 plugin/jcapture/plugin_applet.php create mode 100644 plugin/jcapture/script.js create mode 100644 plugin/jcapture/src/META-INF/services/com.hammurapi.jcapture.VideoEncoder create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/AWTUtilitiesTranslucener.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/AbstractCaptureApplet.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ButtonManager.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/CaptureConfig.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/CaptureFrame.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/CaptureOptionsDialog.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ComponentMover.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ComponentResizer.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/FragmentImpl.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/FrameImpl.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/GraphicsDeviceTranslucener.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ImageToolTip.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/JCaptureApplet.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/MappedImage.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/Movie.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/MovieEditorDialog.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/RecordingControlsFrame.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/Region.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ScreenRecorder.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ScreenShot.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/ShapeImpl.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/SwfEncoder.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/Translucener.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/VideoEncoder.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/WavFile.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/WavFileException.java create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/camera.png create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/mouse.png create mode 100644 plugin/jcapture/src/com/hammurapi/jcapture/toolbar_buttons.swf create mode 100644 plugin/jcapture/upload.php create mode 100644 plugin/justification/Justification.php create mode 100644 plugin/justification/README.md create mode 100644 plugin/justification/add.php create mode 100644 plugin/justification/cron.php create mode 100644 plugin/justification/edit.php create mode 100644 plugin/justification/index.php create mode 100644 plugin/justification/install.php create mode 100644 plugin/justification/justification_by_user.php create mode 100644 plugin/justification/lang/english.php create mode 100644 plugin/justification/lang/french.php create mode 100644 plugin/justification/lang/spanish.php create mode 100644 plugin/justification/list.php create mode 100644 plugin/justification/plugin.php create mode 100644 plugin/justification/set_course.php create mode 100644 plugin/justification/uninstall.php create mode 100644 plugin/justification/view/add.tpl create mode 100644 plugin/justification/view/justification_user_list.tpl create mode 100644 plugin/justification/view/list.tpl create mode 100644 plugin/kannelsms/README.md create mode 100644 plugin/kannelsms/config.php create mode 100644 plugin/kannelsms/index.php create mode 100644 plugin/kannelsms/install.php create mode 100644 plugin/kannelsms/lang/english.php create mode 100644 plugin/kannelsms/lang/spanish.php create mode 100644 plugin/kannelsms/lib/kannelsms.lib.php create mode 100644 plugin/kannelsms/lib/kannelsms_plugin.class.php create mode 100644 plugin/kannelsms/license.txt create mode 100644 plugin/kannelsms/plugin.php create mode 100644 plugin/kannelsms/sms_templates/account_approved_connect.tpl create mode 100644 plugin/kannelsms/sms_templates/account_created_updated_login_password.tpl create mode 100644 plugin/kannelsms/sms_templates/answer_open_question_test_reviewed.tpl create mode 100644 plugin/kannelsms/sms_templates/assignment_been_created_course.tpl create mode 100644 plugin/kannelsms/sms_templates/been_invited_complete_survey_course.tpl create mode 100644 plugin/kannelsms/sms_templates/been_invited_join_group.tpl create mode 100644 plugin/kannelsms/sms_templates/been_subscribed_course.tpl create mode 100644 plugin/kannelsms/sms_templates/check_email_connect_more_info.tpl create mode 100644 plugin/kannelsms/sms_templates/course_opening_request_code_registered.tpl create mode 100644 plugin/kannelsms/sms_templates/course_opening_request_course_code.tpl create mode 100644 plugin/kannelsms/sms_templates/course_opening_request_course_code_approved.tpl create mode 100644 plugin/kannelsms/sms_templates/messages_sent_edited_group_added.tpl create mode 100644 plugin/kannelsms/sms_templates/messages_sent_edited_group_edited.tpl create mode 100644 plugin/kannelsms/sms_templates/new_answer_posted_forum.tpl create mode 100644 plugin/kannelsms/sms_templates/new_course_been_created.tpl create mode 100644 plugin/kannelsms/sms_templates/new_course_suggested_teacher.tpl create mode 100644 plugin/kannelsms/sms_templates/new_file_shared_course_by.tpl create mode 100644 plugin/kannelsms/sms_templates/new_system_announcement_added.tpl create mode 100644 plugin/kannelsms/sms_templates/new_thread_started_forum.tpl create mode 100644 plugin/kannelsms/sms_templates/new_user_pending_approval.tpl create mode 100644 plugin/kannelsms/sms_templates/new_user_subscribed_course.tpl create mode 100644 plugin/kannelsms/sms_templates/password_updated_login_password.tpl create mode 100644 plugin/kannelsms/sms_templates/posted_forum_course.tpl create mode 100644 plugin/kannelsms/sms_templates/received_new_personal_messages.tpl create mode 100644 plugin/kannelsms/sms_templates/reminder_assignment_course_due.tpl create mode 100644 plugin/kannelsms/sms_templates/request_open_course_code_rejected.tpl create mode 100644 plugin/kannelsms/sms_templates/requested_password_change.tpl create mode 100644 plugin/kannelsms/sms_templates/student_answered_test.tpl create mode 100644 plugin/kannelsms/sms_templates/student_answered_test_open_question.tpl create mode 100644 plugin/kannelsms/sms_templates/student_answered_test_voice_question.tpl create mode 100644 plugin/kannelsms/sms_templates/subscribed_session.tpl create mode 100644 plugin/kannelsms/sms_templates/subscribed_session_csv.tpl create mode 100644 plugin/kannelsms/sms_templates/system_announcement_update.tpl create mode 100644 plugin/kannelsms/sms_templates/test_new_system_announcement_added.tpl create mode 100644 plugin/kannelsms/sms_templates/test_system_announcement_update.tpl create mode 100644 plugin/kannelsms/sms_templates/user_answered_inbox_message.tpl create mode 100644 plugin/kannelsms/sms_templates/user_details_modified.tpl create mode 100644 plugin/kannelsms/sms_templates/user_suggested_be_friends.tpl create mode 100644 plugin/kannelsms/sms_templates/user_uploaded_assignment_check.tpl create mode 100644 plugin/kannelsms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl create mode 100644 plugin/kannelsms/sms_templates/user_uploaded_assignment_course.tpl create mode 100644 plugin/kannelsms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl create mode 100644 plugin/kannelsms/sms_templates/welcome_login_password.tpl create mode 100644 plugin/kannelsms/uninstall.php create mode 100644 plugin/kannelsms/vendor/changelog.md create mode 100644 plugin/kannelsms/vendor/exception.php create mode 100644 plugin/kannelsms/vendor/kannelsms_api.php create mode 100644 plugin/kannelsms/vendor/license.txt create mode 100644 plugin/kannelsms/vendor/readme.md create mode 100644 plugin/keycloak/KeycloakPlugin.php create mode 100644 plugin/keycloak/README.md create mode 100644 plugin/keycloak/index.php create mode 100644 plugin/keycloak/lang/english.php create mode 100644 plugin/keycloak/metadata.php create mode 100644 plugin/keycloak/plugin.php create mode 100644 plugin/keycloak/settings.dist.php create mode 100644 plugin/keycloak/start.php create mode 100644 plugin/learning_calendar/LearningCalendarPlugin.php create mode 100644 plugin/learning_calendar/README.md create mode 100644 plugin/learning_calendar/ajax.php create mode 100644 plugin/learning_calendar/calendar.php create mode 100644 plugin/learning_calendar/calendar_users.php create mode 100644 plugin/learning_calendar/install.php create mode 100644 plugin/learning_calendar/lang/english.php create mode 100644 plugin/learning_calendar/lang/french.php create mode 100644 plugin/learning_calendar/lang/spanish.php create mode 100644 plugin/learning_calendar/plugin.php create mode 100644 plugin/learning_calendar/start.php create mode 100644 plugin/learning_calendar/uninstall.php create mode 100644 plugin/learning_calendar/view/calendar.tpl create mode 100644 plugin/learning_calendar/view/start.tpl create mode 100644 plugin/lti_provider/Entity/Platform.php create mode 100644 plugin/lti_provider/Entity/PlatformKey.php create mode 100644 plugin/lti_provider/Entity/Result.php create mode 100644 plugin/lti_provider/LtiProviderPlugin.php create mode 100644 plugin/lti_provider/README.md create mode 100644 plugin/lti_provider/admin.php create mode 100644 plugin/lti_provider/create.php create mode 100644 plugin/lti_provider/db/lti13_cache.php create mode 100644 plugin/lti_provider/db/lti13_cookie.php create mode 100644 plugin/lti_provider/db/lti13_database.php create mode 100644 plugin/lti_provider/delete.php create mode 100644 plugin/lti_provider/edit.php create mode 100644 plugin/lti_provider/install.php create mode 100644 plugin/lti_provider/lang/english.php create mode 100644 plugin/lti_provider/lang/french.php create mode 100644 plugin/lti_provider/lang/spanish.php create mode 100644 plugin/lti_provider/plugin.php create mode 100644 plugin/lti_provider/provider_settings.php create mode 100644 plugin/lti_provider/src/Form/FrmAdd.php create mode 100644 plugin/lti_provider/src/Form/FrmEdit.php create mode 100644 plugin/lti_provider/src/LtiProvider.php create mode 100644 plugin/lti_provider/tool/api/score.php create mode 100644 plugin/lti_provider/tool/jwks.php create mode 100644 plugin/lti_provider/tool/login.php create mode 100644 plugin/lti_provider/tool/logout.php create mode 100644 plugin/lti_provider/tool/start.php create mode 100644 plugin/lti_provider/uninstall.php create mode 100644 plugin/lti_provider/view/add.tpl create mode 100644 plugin/lti_provider/view/provider_admin.tpl create mode 100644 plugin/maintenancemode/MaintenanceModePlugin.php create mode 100644 plugin/maintenancemode/README.md create mode 100644 plugin/maintenancemode/index.php create mode 100644 plugin/maintenancemode/install.php create mode 100644 plugin/maintenancemode/lang/english.php create mode 100644 plugin/maintenancemode/lang/french.php create mode 100644 plugin/maintenancemode/lang/spanish.php create mode 100644 plugin/maintenancemode/plugin.php create mode 100644 plugin/migrationmoodle/README.md create mode 100644 plugin/migrationmoodle/admin.php create mode 100644 plugin/migrationmoodle/install.php create mode 100644 plugin/migrationmoodle/lang/english.php create mode 100644 plugin/migrationmoodle/lang/french.php create mode 100644 plugin/migrationmoodle/plugin.php create mode 100644 plugin/migrationmoodle/run_cli.php create mode 100644 plugin/migrationmoodle/src/Extractor/BaseExtractor.php create mode 100644 plugin/migrationmoodle/src/Extractor/FilterExtractor.php create mode 100644 plugin/migrationmoodle/src/Extractor/LoadedCoursesFilterExtractor.php create mode 100644 plugin/migrationmoodle/src/Extractor/LoadedScormsFilterExtractor.php create mode 100644 plugin/migrationmoodle/src/Extractor/LoadedUsersFilterExtractor.php create mode 100644 plugin/migrationmoodle/src/Interfaces/ExtractorInterface.php create mode 100644 plugin/migrationmoodle/src/Interfaces/LoaderInterface.php create mode 100644 plugin/migrationmoodle/src/Interfaces/TransformPropertyInterface.php create mode 100644 plugin/migrationmoodle/src/Interfaces/TransformerInterface.php create mode 100644 plugin/migrationmoodle/src/Loader/CQuizLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseCategoriesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseFilesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseIntroductionLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseModulesLessonLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseModulesQuizLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseModulesScormLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseModulesUrlLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CourseSectionsLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/CoursesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/FilesForScormScoLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersEssayLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersMatchingLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersMultipleAnswerLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersMultipleChoiceLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersShortAnswerLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonAnswersTrueFalseLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonPagesDocumentLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonPagesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuestionLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuizLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/QuestionCategoriesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/QuestionGapselectLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/QuizzesLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/QuizzesScoresLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/RoleAssignmentsLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/ScormScoLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/SortSectionModuleLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/TrackCourseAccessLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/TrackLoginLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UrlLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLastLoginLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLearnPathLessonAttemptLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLearnPathLessonBranchLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLearnPathLessonTimerLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLearnPathQuizLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserLearnPathsLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserQuestionAttemptLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserQuizAttemptLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UserSessionLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UsersLoader.php create mode 100644 plugin/migrationmoodle/src/Loader/UsersScormsViewLoader.php create mode 100644 plugin/migrationmoodle/src/Messages/ExtractMessage.php create mode 100644 plugin/migrationmoodle/src/Messages/LoadMessage.php create mode 100644 plugin/migrationmoodle/src/Messages/Message.php create mode 100644 plugin/migrationmoodle/src/Messages/TransformMessage.php create mode 100644 plugin/migrationmoodle/src/MigrationMoodleCheckLoginCredentialsHook.php create mode 100644 plugin/migrationmoodle/src/MigrationMoodlePlugin.php create mode 100644 plugin/migrationmoodle/src/Script/BaseScript.php create mode 100644 plugin/migrationmoodle/src/Script/UserLearnPathsProgressScript.php create mode 100644 plugin/migrationmoodle/src/Script/UserScormsProgressScript.php create mode 100644 plugin/migrationmoodle/src/Task/BaseTask.php create mode 100644 plugin/migrationmoodle/src/Task/CQuizTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseCategoriesTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseFilesTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseIntroductionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseModulesLessonTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseModulesQuizTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseModulesScormTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseModulesUrlTask.php create mode 100644 plugin/migrationmoodle/src/Task/CourseSectionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/CoursesTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForCourseIntroductionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForCourseSectionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForLessonAnswersTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForLessonPagesTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForQuizzesTask.php create mode 100644 plugin/migrationmoodle/src/Task/FilesForScormScoesTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersEssayTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersMatchingTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersMultipleAnswerTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersMultipleChoiceTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersShortAnswerTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonAnswersTrueFalseTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonPagesDocumentTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonPagesQuizQuestionTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonPagesQuizTask.php create mode 100644 plugin/migrationmoodle/src/Task/LessonPagesTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionCategoriesTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionGapselectTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionMultiChoiceMultipleTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionMultiChoiceSingleTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionShortAnswerTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuestionsTrueFalseTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuizzesScoresTask.php create mode 100644 plugin/migrationmoodle/src/Task/QuizzesTask.php create mode 100644 plugin/migrationmoodle/src/Task/RoleAssignmentsTask.php create mode 100644 plugin/migrationmoodle/src/Task/ScormScoesTask.php create mode 100644 plugin/migrationmoodle/src/Task/SortSectionModulesTask.php create mode 100644 plugin/migrationmoodle/src/Task/TrackCourseAccessTask.php create mode 100644 plugin/migrationmoodle/src/Task/TrackLoginTask.php create mode 100644 plugin/migrationmoodle/src/Task/UrlsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UserQuestionAttemptsGapselectTask.php create mode 100644 plugin/migrationmoodle/src/Task/UserQuestionAttemptsShortanswerTask.php create mode 100644 plugin/migrationmoodle/src/Task/UserQuestionAttemptsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UserQuestionAttemptsTruefalseTask.php create mode 100644 plugin/migrationmoodle/src/Task/UserSessionsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLastLoginTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLearnPathsLessonAttemptsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLearnPathsLessonBranchTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLearnPathsLessonTimerTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLearnPathsQuizzesTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersLearnPathsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersQuizzesAttemptsTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersScormsViewTask.php create mode 100644 plugin/migrationmoodle/src/Task/UsersTask.php create mode 100644 plugin/migrationmoodle/src/Traits/FileFinderTrait.php create mode 100644 plugin/migrationmoodle/src/Traits/MapTrait.php create mode 100644 plugin/migrationmoodle/src/Transformer/BaseTransformer.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/AuthLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/Copy.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/CourseCategoryLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/CourseCode.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/CourseUserStatus.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/CourseVisibility.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/CoursesArrayLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/DateTimeObject.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/Explode.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/IsFalse.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/Language.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LearnPathItemViewQuizStatus.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LessonAnswersMatchingScore.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LessonPageType.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseCodeLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseFromQuestionCategoryLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleLessonLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizByQuizLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleUrlLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionFromLessonLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedKeyLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizQuestionLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedQuestionLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedQuizLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedScormLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedScormScoLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedUserLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedUserQuizLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/LoadedUserSessionLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/Percentage.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/QuestionGapselectAnswer.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/QuestionType.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/QuizDataTracking.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/QuizQuestionTypeFromLessonPage.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/ReplaceFilePaths.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/ScormScoParentLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/ScormScoTrackData.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/SectionSequenceLookup.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/SessionName.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/Subtract.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserActive.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswer.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerGapselect.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerShortanswer.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerTruefalse.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/UserStatus.php create mode 100644 plugin/migrationmoodle/src/Transformer/Property/WrapHtmlReplacingFilePaths.php create mode 100644 plugin/migrationmoodle/uninstall.php create mode 100644 plugin/mindmap/ajax/mindmap.ajax.php create mode 100644 plugin/mindmap/edit-mindmap/README.md create mode 100644 plugin/mindmap/edit-mindmap/app-base.js create mode 100644 plugin/mindmap/edit-mindmap/build/config.json create mode 100644 plugin/mindmap/edit-mindmap/build/lib/combine.js create mode 100644 plugin/mindmap/edit-mindmap/build/lib/config.js create mode 100644 plugin/mindmap/edit-mindmap/build/lib/dependency.js create mode 100644 plugin/mindmap/edit-mindmap/build/make.js create mode 100644 plugin/mindmap/edit-mindmap/build/makeDeps.js create mode 100644 plugin/mindmap/edit-mindmap/doc/code-structure.md create mode 100644 plugin/mindmap/edit-mindmap/doc/note.md create mode 100644 plugin/mindmap/edit-mindmap/img/mindmap.png create mode 100644 plugin/mindmap/edit-mindmap/img/mindmap128.png create mode 100644 plugin/mindmap/edit-mindmap/img/mindmap128gray.png create mode 100644 plugin/mindmap/edit-mindmap/index.php create mode 100644 plugin/mindmap/edit-mindmap/mindMap.js create mode 100644 plugin/mindmap/edit-mindmap/mindMap.txt create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/DemoTransformsAnimations.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/FullPageAnimationsPrefixed.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/app.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.min.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/bootstrap.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/bootstrap.min.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/help.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/css/qunit.css create mode 100644 plugin/mindmap/edit-mindmap/vendor/help/introduce.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/help/license.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/help/map.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/help/node.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/html/index.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/html/index2.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/html/template.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/bg.gif create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings-white.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/append_child_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/copy_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/create_new_map.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/create_root_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/cut_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/delete.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/edit_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/help.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_disk.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_storage.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/paste_node.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/redo1.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/redo2.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/save_map_in_disk.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/save_map_in_storage.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/undo1.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/img/help/undo2.png create mode 100644 plugin/mindmap/edit-mindmap/vendor/index.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/MapManager.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/MapsManager.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/base.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/branch.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/caption.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/command.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/commandcontroller.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/commandstack.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/component.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/composition.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/deps.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/help.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/jquery.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/ajax.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/base.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class/class.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class/composition.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class/dialog.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class/eventtarget.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/class/uicomponent.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/data.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/deps.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/dom.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/events.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/eventtarget.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/helper/es5-shim.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/helper/json2.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/kampfer/support.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/map.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/menu.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/mindmap.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/node.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/openmapdialog.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/plugins/BlobBuilder.min.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/plugins/FileSaver.min.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/plugins/modernizr-latest.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/plugins/mousetrap.min.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/plugins/store.min.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/radio.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/renamemapdialog.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/toolbar.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/js/window.js create mode 100644 plugin/mindmap/edit-mindmap/vendor/map.json create mode 100644 plugin/mindmap/edit-mindmap/vendor/mindmap.mf create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_composition.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_domDepth.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_events.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_index.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_parentNode_null.html create mode 100644 plugin/mindmap/edit-mindmap/vendor/test/test_template.html create mode 100644 plugin/mindmap/img/22/mindmap.png create mode 100644 plugin/mindmap/img/22/mindmap_na.png create mode 100644 plugin/mindmap/img/32/mindmap.png create mode 100644 plugin/mindmap/img/32/mindmap_na.png create mode 100644 plugin/mindmap/img/64/mindmap.png create mode 100644 plugin/mindmap/img/64/mindmap_na.png create mode 100644 plugin/mindmap/img/edit.png create mode 100644 plugin/mindmap/img/eyes.png create mode 100644 plugin/mindmap/img/mindmap.png create mode 100644 plugin/mindmap/img/mindmap128.png create mode 100644 plugin/mindmap/img/mindmap24.png create mode 100644 plugin/mindmap/img/mindmap600.png create mode 100644 plugin/mindmap/img/sumin.png create mode 100644 plugin/mindmap/inc/action.switch.php create mode 100644 plugin/mindmap/inc/edit.form.php create mode 100644 plugin/mindmap/index.php create mode 100644 plugin/mindmap/install.php create mode 100644 plugin/mindmap/lang/english.php create mode 100644 plugin/mindmap/lang/french.php create mode 100644 plugin/mindmap/lang/spanish.php create mode 100644 plugin/mindmap/list.php create mode 100644 plugin/mindmap/mindmap_plugin.class.php create mode 100644 plugin/mindmap/plugin.php create mode 100644 plugin/mindmap/resources/img/docx.png create mode 100644 plugin/mindmap/resources/img/download.png create mode 100644 plugin/mindmap/resources/img/file-docx.png create mode 100644 plugin/mindmap/resources/img/file-none.png create mode 100644 plugin/mindmap/resources/img/file-pdf.png create mode 100644 plugin/mindmap/resources/img/forum_listview.png create mode 100644 plugin/mindmap/resources/img/g91884.png create mode 100644 plugin/mindmap/resources/img/lock.png create mode 100644 plugin/mindmap/resources/img/pdf.png create mode 100644 plugin/mindmap/resources/img/reading-comprehension_na.png create mode 100644 plugin/mindmap/resources/img/statistics.png create mode 100644 plugin/mindmap/resources/img/stats.png create mode 100644 plugin/mindmap/resources/img/survey_reporting_overall.png create mode 100644 plugin/mindmap/resources/js/jquery.dataTables.min.js create mode 100644 plugin/mindmap/start.php create mode 100644 plugin/mindmap/uninstall.php create mode 100644 plugin/mindmap/view/list.tpl create mode 100644 plugin/nosearchindex/NoSearchIndex.php create mode 100644 plugin/nosearchindex/README.md create mode 100644 plugin/nosearchindex/index.php create mode 100644 plugin/nosearchindex/install.php create mode 100644 plugin/nosearchindex/lang/english.php create mode 100644 plugin/nosearchindex/plugin.php create mode 100644 plugin/notebookteacher/README.md create mode 100644 plugin/notebookteacher/config.php create mode 100644 plugin/notebookteacher/database.php create mode 100644 plugin/notebookteacher/index.php create mode 100644 plugin/notebookteacher/install.php create mode 100644 plugin/notebookteacher/lang/english.php create mode 100644 plugin/notebookteacher/lang/spanish.php create mode 100644 plugin/notebookteacher/plugin.php create mode 100644 plugin/notebookteacher/resources/img/22/notebookteacher.png create mode 100644 plugin/notebookteacher/resources/img/32/notebookteacher.png create mode 100644 plugin/notebookteacher/resources/img/32/notebookteacher_na.png create mode 100644 plugin/notebookteacher/resources/img/64/notebookteacher.png create mode 100644 plugin/notebookteacher/resources/img/64/notebookteacher_na.png create mode 100644 plugin/notebookteacher/src/NotebookTeacher.php create mode 100644 plugin/notebookteacher/src/NotebookTeacherPlugin.php create mode 100644 plugin/notebookteacher/src/index.php create mode 100644 plugin/notebookteacher/start.php create mode 100644 plugin/notebookteacher/uninstall.php create mode 100644 plugin/notebookteacher/update.php create mode 100644 plugin/oauth2/README.md create mode 100644 plugin/oauth2/config.dist.php create mode 100644 plugin/oauth2/index.php create mode 100644 plugin/oauth2/install.php create mode 100644 plugin/oauth2/lang/english.php create mode 100644 plugin/oauth2/layout/login_form.tpl create mode 100644 plugin/oauth2/localLogin/images/footer.png create mode 100644 plugin/oauth2/localLogin/images/header.png create mode 100644 plugin/oauth2/localLogin/login.php create mode 100644 plugin/oauth2/login.php create mode 100644 plugin/oauth2/plugin.php create mode 100644 plugin/oauth2/redirect_info.php create mode 100644 plugin/oauth2/src/OAuth2.php create mode 100644 plugin/oauth2/src/callback.php create mode 100644 plugin/oauth2/src/oidc_login.php create mode 100644 plugin/oauth2/view/block.tpl create mode 100644 plugin/oauth2/view/login.tpl create mode 100644 plugin/olpc_peru_filter/README.md create mode 100644 plugin/olpc_peru_filter/config.php create mode 100644 plugin/olpc_peru_filter/index.php create mode 100644 plugin/olpc_peru_filter/install.php create mode 100644 plugin/olpc_peru_filter/lang/english.php create mode 100644 plugin/olpc_peru_filter/lang/spanish.php create mode 100644 plugin/olpc_peru_filter/lib/olpc_peru_filter_plugin.class.php create mode 100644 plugin/olpc_peru_filter/plugin.php create mode 100644 plugin/olpc_peru_filter/uninstall.php create mode 100644 plugin/onlyoffice/.php-cs-fixer.dist.php create mode 100644 plugin/onlyoffice/.php-cs-fixer.php create mode 100644 plugin/onlyoffice/AUTHORS.md create mode 100644 plugin/onlyoffice/CHANGELOG.md create mode 100644 plugin/onlyoffice/LICENSE.txt create mode 100644 plugin/onlyoffice/README.md create mode 100644 plugin/onlyoffice/ajax/saveas.php create mode 100644 plugin/onlyoffice/assets/AUTHORS.md create mode 100644 plugin/onlyoffice/assets/LICENSE.txt create mode 100644 plugin/onlyoffice/assets/README.md create mode 100644 plugin/onlyoffice/assets/ar/docx.zip create mode 100644 plugin/onlyoffice/assets/ar/pdf.zip create mode 100644 plugin/onlyoffice/assets/ar/pptx.zip create mode 100644 plugin/onlyoffice/assets/ar/xlsx.zip create mode 100644 plugin/onlyoffice/assets/az/docx.zip create mode 100644 plugin/onlyoffice/assets/az/pdf.zip create mode 100644 plugin/onlyoffice/assets/az/pptx.zip create mode 100644 plugin/onlyoffice/assets/az/xlsx.zip create mode 100644 plugin/onlyoffice/assets/bg/docx.zip create mode 100644 plugin/onlyoffice/assets/bg/pdf.zip create mode 100644 plugin/onlyoffice/assets/bg/pptx.zip create mode 100644 plugin/onlyoffice/assets/bg/xlsx.zip create mode 100644 plugin/onlyoffice/assets/cs/docx.zip create mode 100644 plugin/onlyoffice/assets/cs/pdf.zip create mode 100644 plugin/onlyoffice/assets/cs/pptx.zip create mode 100644 plugin/onlyoffice/assets/cs/xlsx.zip create mode 100644 plugin/onlyoffice/assets/de/docx.zip create mode 100644 plugin/onlyoffice/assets/de/pdf.zip create mode 100644 plugin/onlyoffice/assets/de/pptx.zip create mode 100644 plugin/onlyoffice/assets/de/xlsx.zip create mode 100644 plugin/onlyoffice/assets/default/docx.zip create mode 100644 plugin/onlyoffice/assets/default/pdf.zip create mode 100644 plugin/onlyoffice/assets/default/pptx.zip create mode 100644 plugin/onlyoffice/assets/default/xlsx.zip create mode 100644 plugin/onlyoffice/assets/el/docx.zip create mode 100644 plugin/onlyoffice/assets/el/pdf.zip create mode 100644 plugin/onlyoffice/assets/el/pptx.zip create mode 100644 plugin/onlyoffice/assets/el/xlsx.zip create mode 100644 plugin/onlyoffice/assets/en-GB/docx.zip create mode 100644 plugin/onlyoffice/assets/en-GB/pdf.zip create mode 100644 plugin/onlyoffice/assets/en-GB/pptx.zip create mode 100644 plugin/onlyoffice/assets/en-GB/xlsx.zip create mode 100644 plugin/onlyoffice/assets/en/docx.zip create mode 100644 plugin/onlyoffice/assets/en/pdf.zip create mode 100644 plugin/onlyoffice/assets/en/pptx.zip create mode 100644 plugin/onlyoffice/assets/en/xlsx.zip create mode 100644 plugin/onlyoffice/assets/es/docx.zip create mode 100644 plugin/onlyoffice/assets/es/pdf.zip create mode 100644 plugin/onlyoffice/assets/es/pptx.zip create mode 100644 plugin/onlyoffice/assets/es/xlsx.zip create mode 100644 plugin/onlyoffice/assets/eu/docx.zip create mode 100644 plugin/onlyoffice/assets/eu/pdf.zip create mode 100644 plugin/onlyoffice/assets/eu/pptx.zip create mode 100644 plugin/onlyoffice/assets/eu/xlsx.zip create mode 100644 plugin/onlyoffice/assets/fi/docx.zip create mode 100644 plugin/onlyoffice/assets/fi/pdf.zip create mode 100644 plugin/onlyoffice/assets/fi/pptx.zip create mode 100644 plugin/onlyoffice/assets/fi/xlsx.zip create mode 100644 plugin/onlyoffice/assets/fr/docx.zip create mode 100644 plugin/onlyoffice/assets/fr/pdf.zip create mode 100644 plugin/onlyoffice/assets/fr/pptx.zip create mode 100644 plugin/onlyoffice/assets/fr/xlsx.zip create mode 100644 plugin/onlyoffice/assets/gl/docx.zip create mode 100644 plugin/onlyoffice/assets/gl/pdf.zip create mode 100644 plugin/onlyoffice/assets/gl/pptx.zip create mode 100644 plugin/onlyoffice/assets/gl/xlsx.zip create mode 100644 plugin/onlyoffice/assets/he/docx.zip create mode 100644 plugin/onlyoffice/assets/he/pdf.zip create mode 100644 plugin/onlyoffice/assets/he/pptx.zip create mode 100644 plugin/onlyoffice/assets/he/xlsx.zip create mode 100644 plugin/onlyoffice/assets/hy/docx.zip create mode 100644 plugin/onlyoffice/assets/hy/pdf.zip create mode 100644 plugin/onlyoffice/assets/hy/pptx.zip create mode 100644 plugin/onlyoffice/assets/hy/xlsx.zip create mode 100644 plugin/onlyoffice/assets/it/docx.zip create mode 100644 plugin/onlyoffice/assets/it/pdf.zip create mode 100644 plugin/onlyoffice/assets/it/pptx.zip create mode 100644 plugin/onlyoffice/assets/it/xlsx.zip create mode 100644 plugin/onlyoffice/assets/ja/docx.zip create mode 100644 plugin/onlyoffice/assets/ja/pdf.zip create mode 100644 plugin/onlyoffice/assets/ja/pptx.zip create mode 100644 plugin/onlyoffice/assets/ja/xlsx.zip create mode 100644 plugin/onlyoffice/assets/ko/docx.zip create mode 100644 plugin/onlyoffice/assets/ko/pdf.zip create mode 100644 plugin/onlyoffice/assets/ko/pptx.zip create mode 100644 plugin/onlyoffice/assets/ko/xlsx.zip create mode 100644 plugin/onlyoffice/assets/lv/docx.zip create mode 100644 plugin/onlyoffice/assets/lv/pdf.zip create mode 100644 plugin/onlyoffice/assets/lv/pptx.zip create mode 100644 plugin/onlyoffice/assets/lv/xlsx.zip create mode 100644 plugin/onlyoffice/assets/ms/docx.zip create mode 100644 plugin/onlyoffice/assets/ms/pdf.zip create mode 100644 plugin/onlyoffice/assets/ms/pptx.zip create mode 100644 plugin/onlyoffice/assets/ms/xlsx.zip create mode 100644 plugin/onlyoffice/assets/nl/docx.zip create mode 100644 plugin/onlyoffice/assets/nl/pdf.zip create mode 100644 plugin/onlyoffice/assets/nl/pptx.zip create mode 100644 plugin/onlyoffice/assets/nl/xlsx.zip create mode 100644 plugin/onlyoffice/assets/no/docx.zip create mode 100644 plugin/onlyoffice/assets/no/pdf.zip create mode 100644 plugin/onlyoffice/assets/no/pptx.zip create mode 100644 plugin/onlyoffice/assets/no/xlsx.zip create mode 100644 plugin/onlyoffice/assets/pl/docx.zip create mode 100644 plugin/onlyoffice/assets/pl/pdf.zip create mode 100644 plugin/onlyoffice/assets/pl/pptx.zip create mode 100644 plugin/onlyoffice/assets/pl/xlsx.zip create mode 100644 plugin/onlyoffice/assets/pt-BR/docx.zip create mode 100644 plugin/onlyoffice/assets/pt-BR/pdf.zip create mode 100644 plugin/onlyoffice/assets/pt-BR/pptx.zip create mode 100644 plugin/onlyoffice/assets/pt-BR/xlsx.zip create mode 100644 plugin/onlyoffice/assets/pt/docx.zip create mode 100644 plugin/onlyoffice/assets/pt/pdf.zip create mode 100644 plugin/onlyoffice/assets/pt/pptx.zip create mode 100644 plugin/onlyoffice/assets/pt/xlsx.zip create mode 100644 plugin/onlyoffice/assets/ru/docx.zip create mode 100644 plugin/onlyoffice/assets/ru/pdf.zip create mode 100644 plugin/onlyoffice/assets/ru/pptx.zip create mode 100644 plugin/onlyoffice/assets/ru/xlsx.zip create mode 100644 plugin/onlyoffice/assets/si/docx.zip create mode 100644 plugin/onlyoffice/assets/si/pdf.zip create mode 100644 plugin/onlyoffice/assets/si/pptx.zip create mode 100644 plugin/onlyoffice/assets/si/xlsx.zip create mode 100644 plugin/onlyoffice/assets/sk/docx.zip create mode 100644 plugin/onlyoffice/assets/sk/pdf.zip create mode 100644 plugin/onlyoffice/assets/sk/pptx.zip create mode 100644 plugin/onlyoffice/assets/sk/xlsx.zip create mode 100644 plugin/onlyoffice/assets/sl/docx.zip create mode 100644 plugin/onlyoffice/assets/sl/pdf.zip create mode 100644 plugin/onlyoffice/assets/sl/pptx.zip create mode 100644 plugin/onlyoffice/assets/sl/xlsx.zip create mode 100644 plugin/onlyoffice/assets/sr-Cyrl-RS/docx.zip create mode 100644 plugin/onlyoffice/assets/sr-Cyrl-RS/pdf.zip create mode 100644 plugin/onlyoffice/assets/sr-Cyrl-RS/pptx.zip create mode 100644 plugin/onlyoffice/assets/sr-Cyrl-RS/xlsx.zip create mode 100644 plugin/onlyoffice/assets/sr/docx.zip create mode 100644 plugin/onlyoffice/assets/sr/pdf.zip create mode 100644 plugin/onlyoffice/assets/sr/pptx.zip create mode 100644 plugin/onlyoffice/assets/sr/xlsx.zip create mode 100644 plugin/onlyoffice/assets/sv/docx.zip create mode 100644 plugin/onlyoffice/assets/sv/pdf.zip create mode 100644 plugin/onlyoffice/assets/sv/pptx.zip create mode 100644 plugin/onlyoffice/assets/sv/xlsx.zip create mode 100644 plugin/onlyoffice/assets/tr/docx.zip create mode 100644 plugin/onlyoffice/assets/tr/pdf.zip create mode 100644 plugin/onlyoffice/assets/tr/pptx.zip create mode 100644 plugin/onlyoffice/assets/tr/xlsx.zip create mode 100644 plugin/onlyoffice/assets/uk/docx.zip create mode 100644 plugin/onlyoffice/assets/uk/pdf.zip create mode 100644 plugin/onlyoffice/assets/uk/pptx.zip create mode 100644 plugin/onlyoffice/assets/uk/xlsx.zip create mode 100644 plugin/onlyoffice/assets/vi/docx.zip create mode 100644 plugin/onlyoffice/assets/vi/pdf.zip create mode 100644 plugin/onlyoffice/assets/vi/pptx.zip create mode 100644 plugin/onlyoffice/assets/vi/xlsx.zip create mode 100644 plugin/onlyoffice/assets/zh-TW/docx.zip create mode 100644 plugin/onlyoffice/assets/zh-TW/pdf.zip create mode 100644 plugin/onlyoffice/assets/zh-TW/pptx.zip create mode 100644 plugin/onlyoffice/assets/zh-TW/xlsx.zip create mode 100644 plugin/onlyoffice/assets/zh/docx.zip create mode 100644 plugin/onlyoffice/assets/zh/pdf.zip create mode 100644 plugin/onlyoffice/assets/zh/pptx.zip create mode 100644 plugin/onlyoffice/assets/zh/xlsx.zip create mode 100644 plugin/onlyoffice/callback.php create mode 100644 plugin/onlyoffice/create.php create mode 100644 plugin/onlyoffice/editor.php create mode 100644 plugin/onlyoffice/error.php create mode 100644 plugin/onlyoffice/install.php create mode 100644 plugin/onlyoffice/lang/bulgarian.php create mode 100644 plugin/onlyoffice/lang/dutch.php create mode 100644 plugin/onlyoffice/lang/english.php create mode 100644 plugin/onlyoffice/lang/french.php create mode 100644 plugin/onlyoffice/lang/german.php create mode 100644 plugin/onlyoffice/lang/greek.php create mode 100644 plugin/onlyoffice/lang/italian.php create mode 100644 plugin/onlyoffice/lang/polish.php create mode 100644 plugin/onlyoffice/lang/portuguese.php create mode 100644 plugin/onlyoffice/lang/russian.php create mode 100644 plugin/onlyoffice/lang/spanish.php create mode 100644 plugin/onlyoffice/layout/get_docs_cloud_banner.tpl create mode 100644 plugin/onlyoffice/lib/langManager.php create mode 100644 plugin/onlyoffice/lib/onlyofficeActionObserver.php create mode 100644 plugin/onlyoffice/lib/onlyofficeAppRequests.php create mode 100644 plugin/onlyoffice/lib/onlyofficeAppSettings.php create mode 100644 plugin/onlyoffice/lib/onlyofficeCallbackService.php create mode 100644 plugin/onlyoffice/lib/onlyofficeConfigService.php create mode 100644 plugin/onlyoffice/lib/onlyofficeDocumentManager.php create mode 100644 plugin/onlyoffice/lib/onlyofficeFormatsManager.php create mode 100644 plugin/onlyoffice/lib/onlyofficeHttpClient.php create mode 100644 plugin/onlyoffice/lib/onlyofficeItemActionObserver.php create mode 100644 plugin/onlyoffice/lib/onlyofficeItemViewObserver.php create mode 100644 plugin/onlyoffice/lib/onlyofficeJwtManager.php create mode 100644 plugin/onlyoffice/lib/onlyofficePlugin.php create mode 100644 plugin/onlyoffice/lib/onlyofficeSettingsFormBuilder.php create mode 100644 plugin/onlyoffice/lib/onlyofficeTools.php create mode 100644 plugin/onlyoffice/lib/templateManager.php create mode 100644 plugin/onlyoffice/plugin.php create mode 100644 plugin/onlyoffice/resources/css/docsCloudBanner.css create mode 100644 plugin/onlyoffice/resources/get-editors-background.svg create mode 100644 plugin/onlyoffice/resources/get-editors-pic.svg create mode 100644 plugin/onlyoffice/resources/onlyoffice_create.png create mode 100644 plugin/onlyoffice/resources/onlyoffice_edit.png create mode 100644 plugin/onlyoffice/resources/onlyoffice_view.png create mode 100644 plugin/onlyoffice/uninstall.php create mode 100644 plugin/onlyoffice/vendor/autoload.php create mode 100644 plugin/onlyoffice/vendor/composer/ClassLoader.php create mode 100644 plugin/onlyoffice/vendor/composer/InstalledVersions.php create mode 100644 plugin/onlyoffice/vendor/composer/LICENSE create mode 100644 plugin/onlyoffice/vendor/composer/autoload_classmap.php create mode 100644 plugin/onlyoffice/vendor/composer/autoload_files.php create mode 100644 plugin/onlyoffice/vendor/composer/autoload_namespaces.php create mode 100644 plugin/onlyoffice/vendor/composer/autoload_psr4.php create mode 100644 plugin/onlyoffice/vendor/composer/autoload_real.php create mode 100644 plugin/onlyoffice/vendor/composer/autoload_static.php create mode 100644 plugin/onlyoffice/vendor/composer/installed.json create mode 100644 plugin/onlyoffice/vendor/composer/installed.php create mode 100644 plugin/onlyoffice/vendor/composer/platform_check.php create mode 100644 plugin/onlyoffice/vendor/graham-campbell/result-type/LICENSE create mode 100644 plugin/onlyoffice/vendor/graham-campbell/result-type/composer.json create mode 100644 plugin/onlyoffice/vendor/graham-campbell/result-type/src/Error.php create mode 100644 plugin/onlyoffice/vendor/graham-campbell/result-type/src/Result.php create mode 100644 plugin/onlyoffice/vendor/graham-campbell/result-type/src/Success.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/.env.example create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/.gitignore create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/AUTHORS.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/CHANGELOG.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/LICENSE create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/README.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/composer.json create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/phpcs.xml create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-formats/AUTHORS.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-formats/CHANGELOG.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-formats/LICENSE create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-formats/README.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-formats/onlyoffice-docs-formats.json create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/AUTHORS.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/LICENSE create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/README.md create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ar-SA/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ar-SA/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ar-SA/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ar-SA/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/az-Latn-AZ/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/az-Latn-AZ/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/az-Latn-AZ/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/az-Latn-AZ/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/bg-BG/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/bg-BG/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/bg-BG/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/bg-BG/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/cs-CZ/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/cs-CZ/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/cs-CZ/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/cs-CZ/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/de-DE/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/de-DE/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/de-DE/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/de-DE/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/default/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/default/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/default/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/default/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/el-GR/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/el-GR/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/el-GR/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/el-GR/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-GB/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-GB/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-GB/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-GB/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-US/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-US/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-US/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/en-US/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/es-ES/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/es-ES/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/es-ES/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/es-ES/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/eu-ES/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/eu-ES/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/eu-ES/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/eu-ES/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fi-FI/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fi-FI/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fi-FI/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fi-FI/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fr-FR/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fr-FR/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fr-FR/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/fr-FR/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/gl-ES/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/gl-ES/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/gl-ES/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/gl-ES/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/he-IL/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/he-IL/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/he-IL/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/he-IL/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/hy-AM/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/hy-AM/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/hy-AM/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/hy-AM/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/it-IT/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/it-IT/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/it-IT/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/it-IT/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ja-JP/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ja-JP/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ja-JP/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ja-JP/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ko-KR/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ko-KR/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ko-KR/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ko-KR/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/lv-LV/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/lv-LV/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/lv-LV/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/lv-LV/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ms-MY/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ms-MY/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ms-MY/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ms-MY/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nb-NO/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nb-NO/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nb-NO/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nb-NO/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nl-NL/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nl-NL/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nl-NL/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/nl-NL/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pl-PL/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pl-PL/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pl-PL/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pl-PL/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-BR/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-BR/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-BR/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-BR/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-PT/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-PT/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-PT/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/pt-PT/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ru-RU/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ru-RU/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ru-RU/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/ru-RU/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/si-LK/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/si-LK/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/si-LK/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/si-LK/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sk-SK/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sk-SK/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sk-SK/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sk-SK/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sl-SI/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sl-SI/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sl-SI/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sl-SI/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Cyrl-RS/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Cyrl-RS/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Cyrl-RS/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Cyrl-RS/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Latn-RS/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Latn-RS/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Latn-RS/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sr-Latn-RS/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sv-SE/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sv-SE/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sv-SE/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/sv-SE/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/tr-TR/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/tr-TR/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/tr-TR/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/tr-TR/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/uk-UA/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/uk-UA/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/uk-UA/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/uk-UA/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/vi-VN/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/vi-VN/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/vi-VN/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/vi-VN/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-CN/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-CN/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-CN/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-CN/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-TW/new.docx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-TW/new.pdf create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-TW/new.pptx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/resources/assets/document-templates/zh-TW/new.xlsx create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Document/DocumentManager.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Document/DocumentManagerInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Formats/FormatsManager.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Formats/FormatsManagerInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Security/JwtManager.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Security/JwtManagerInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Settings/SettingsManager.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Manager/Settings/SettingsManagerInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Anonymous.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Callback.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CallbackAction.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CallbackActionType.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CallbackDocStatus.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CallbackForceSaveType.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Changes.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CoEditing.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CoEditingMode.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/CommentGroups.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Config.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/ConvertRequest.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/ConvertRequestThumbnail.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Customer.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Customization.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/DocEditorConfig.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Document.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/DocumentType.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/EditorsMode.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Embedded.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Features.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Format.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/GoBack.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/History.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Info.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/JsonSerializable.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Logo.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/MacrosMode.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Permissions.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Recent.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/ReferenceData.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Review.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/ReviewDisplay.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/SharingSettings.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/SharingSettingsPermissions.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Template.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Toolbar.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Type.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/Unit.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Models/User.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/Callback/CallbackService.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/Callback/CallbackServiceInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/DocEditorConfig/DocEditorConfigService.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/DocEditorConfig/DocEditorConfigServiceInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/Request/HttpClientInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/Request/RequestService.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Service/Request/RequestServiceInterface.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Util/BasicEnum.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Util/CommandResponseError.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Util/CommonError.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Util/ConvertResponseError.php create mode 100644 plugin/onlyoffice/vendor/onlyoffice/docs-integration-sdk/src/Util/EnvUtil.php create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/LICENSE create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/composer.json create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/src/PhpOption/None.php create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/src/PhpOption/Option.php create mode 100644 plugin/onlyoffice/vendor/phpoption/phpoption/src/PhpOption/Some.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/Ctype.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/LICENSE create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/README.md create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/bootstrap.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/bootstrap80.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-ctype/composer.json create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/README.md create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/bootstrap80.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-mbstring/composer.json create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/LICENSE create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Php80.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/PhpToken.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/README.md create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/bootstrap.php create mode 100644 plugin/onlyoffice/vendor/symfony/polyfill-php80/composer.json create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/LICENSE create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/composer.json create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Dotenv.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Loader/Loader.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Loader/Resolver.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/Entry.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/Lexer.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/Lines.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/Parser.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Parser/Value.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/File/Paths.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/File/Reader.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/FileStore.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Store/StringStore.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Util/Regex.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Util/Str.php create mode 100644 plugin/onlyoffice/vendor/vlucas/phpdotenv/src/Validator.php create mode 100644 plugin/openmeetings/README.md create mode 100644 plugin/openmeetings/config.php create mode 100644 plugin/openmeetings/course_index.php create mode 100644 plugin/openmeetings/index.php create mode 100644 plugin/openmeetings/install.php create mode 100644 plugin/openmeetings/lang/english.php create mode 100644 plugin/openmeetings/lang/french.php create mode 100644 plugin/openmeetings/lang/spanish.php create mode 100644 plugin/openmeetings/lib/openmeetings.class.php create mode 100644 plugin/openmeetings/lib/openmeetings_api.php create mode 100644 plugin/openmeetings/lib/openmeetings_gateway.php create mode 100644 plugin/openmeetings/lib/openmeetings_plugin.class.php create mode 100644 plugin/openmeetings/lib/openmeetings_rest_service.php create mode 100644 plugin/openmeetings/lib/room.class.php create mode 100644 plugin/openmeetings/lib/session.class.php create mode 100644 plugin/openmeetings/lib/user.class.php create mode 100644 plugin/openmeetings/listing.php create mode 100644 plugin/openmeetings/listing.tpl create mode 100644 plugin/openmeetings/plugin.php create mode 100644 plugin/openmeetings/start.php create mode 100644 plugin/openmeetings/uninstall.php create mode 100644 plugin/pausetraining/PauseTraining.php create mode 100644 plugin/pausetraining/README.md create mode 100644 plugin/pausetraining/config.php create mode 100644 plugin/pausetraining/cron.php create mode 100644 plugin/pausetraining/cronTest.php create mode 100644 plugin/pausetraining/index.php create mode 100644 plugin/pausetraining/install.php create mode 100644 plugin/pausetraining/lang/english.php create mode 100644 plugin/pausetraining/lang/french.php create mode 100644 plugin/pausetraining/lang/spanish.php create mode 100644 plugin/pausetraining/plugin.php create mode 100644 plugin/pausetraining/view/notification_content.tpl create mode 100644 plugin/pens/COPYING create mode 100644 plugin/pens/README.md create mode 100644 plugin/pens/chamilo_pens.php create mode 100644 plugin/pens/install.php create mode 100644 plugin/pens/lib/COPYING create mode 100644 plugin/pens/lib/PENSPlugin.php create mode 100644 plugin/pens/lib/README.textile create mode 100644 plugin/pens/lib/pens.php create mode 100644 plugin/pens/lib/pens/lib/rfc2396regexes.php create mode 100644 plugin/pens/lib/pens/pens_client.php create mode 100644 plugin/pens/lib/pens/pens_config.php create mode 100644 plugin/pens/lib/pens/pens_controller.php create mode 100644 plugin/pens/lib/pens/pens_exception.php create mode 100644 plugin/pens/lib/pens/pens_message.php create mode 100644 plugin/pens/lib/pens/pens_package_handler.php create mode 100644 plugin/pens/lib/pens/pens_request.php create mode 100644 plugin/pens/lib/pens/pens_request_alert.php create mode 100644 plugin/pens/lib/pens/pens_request_collect.php create mode 100644 plugin/pens/lib/pens/pens_request_factory.php create mode 100644 plugin/pens/lib/pens/pens_request_handler.php create mode 100644 plugin/pens/lib/pens/pens_request_receipt.php create mode 100644 plugin/pens/lib/pens/pens_response.php create mode 100644 plugin/pens/lib/pens/pens_server.php create mode 100644 plugin/pens/lib/pens_client_test.php create mode 100644 plugin/pens/lib/pens_server_test.php create mode 100644 plugin/pens/lib/tests/test_pens_package_handler.php create mode 100644 plugin/pens/lib/tests/test_pens_request.php create mode 100644 plugin/pens/lib/tests/test_pens_response.php create mode 100644 plugin/pens/lib/tests/test_pens_server.php create mode 100644 plugin/pens/pens.php create mode 100644 plugin/pens/plugin.php create mode 100644 plugin/pens/uninstall.php create mode 100644 plugin/positioning/README.md create mode 100644 plugin/positioning/img/positioning-line.png create mode 100644 plugin/positioning/img/positioning-line.svg create mode 100644 plugin/positioning/index.php create mode 100644 plugin/positioning/install.php create mode 100644 plugin/positioning/lang/english.php create mode 100644 plugin/positioning/lang/french.php create mode 100644 plugin/positioning/lang/spanish.php create mode 100644 plugin/positioning/plugin.php create mode 100644 plugin/positioning/src/Positioning.php create mode 100644 plugin/positioning/start.php create mode 100644 plugin/positioning/start_student.php create mode 100644 plugin/positioning/uninstall.php create mode 100644 plugin/positioning/view/start.tpl create mode 100644 plugin/positioning/view/start_student.tpl create mode 100644 plugin/questionoptionsevaluation/QuestionOptionsEvaluationPlugin.php create mode 100644 plugin/questionoptionsevaluation/README.md create mode 100644 plugin/questionoptionsevaluation/evaluation.php create mode 100644 plugin/questionoptionsevaluation/install.php create mode 100644 plugin/questionoptionsevaluation/lang/english.php create mode 100644 plugin/questionoptionsevaluation/lang/spanish.php create mode 100644 plugin/questionoptionsevaluation/plugin.php create mode 100644 plugin/questionoptionsevaluation/uninstall.php create mode 100644 plugin/redirection/README.md create mode 100644 plugin/redirection/RedirectionPlugin.php create mode 100644 plugin/redirection/admin.php create mode 100644 plugin/redirection/config.php create mode 100644 plugin/redirection/index.php create mode 100644 plugin/redirection/install.php create mode 100644 plugin/redirection/lang/english.php create mode 100644 plugin/redirection/lang/french.php create mode 100644 plugin/redirection/lang/spanish.php create mode 100644 plugin/redirection/plugin.php create mode 100644 plugin/redirection/uninstall.php create mode 100644 plugin/remedial_course/README.md create mode 100644 plugin/remedial_course/RemedialCoursePlugin.php create mode 100644 plugin/remedial_course/index.php create mode 100644 plugin/remedial_course/install.php create mode 100644 plugin/remedial_course/lang/english.php create mode 100644 plugin/remedial_course/lang/spanish.php create mode 100644 plugin/remedial_course/plugin.php create mode 100644 plugin/remedial_course/uninstall.php create mode 100644 plugin/resubscription/README.md create mode 100644 plugin/resubscription/config.php create mode 100644 plugin/resubscription/index.php create mode 100644 plugin/resubscription/install.php create mode 100644 plugin/resubscription/lang/english.php create mode 100644 plugin/resubscription/lang/french.php create mode 100644 plugin/resubscription/lang/spanish.php create mode 100644 plugin/resubscription/plugin.php create mode 100644 plugin/resubscription/src/HookResubscription.php create mode 100644 plugin/resubscription/src/Resubscription.php create mode 100644 plugin/resubscription/uninstall.php create mode 100644 plugin/rss/README.md create mode 100644 plugin/rss/index.php create mode 100644 plugin/rss/lang/english.php create mode 100644 plugin/rss/lang/french.php create mode 100644 plugin/rss/lang/spanish.php create mode 100644 plugin/rss/lib/rss_plugin.class.php create mode 100644 plugin/rss/plugin.php create mode 100644 plugin/search_course/README.md create mode 100644 plugin/search_course/index.php create mode 100644 plugin/search_course/lang/english.php create mode 100644 plugin/search_course/lang/french.php create mode 100644 plugin/search_course/lib/register_course_widget.class.php create mode 100644 plugin/search_course/lib/search_course_plugin.class.php create mode 100644 plugin/search_course/lib/search_course_widget.class.php create mode 100644 plugin/search_course/plugin.php create mode 100644 plugin/send_notification_new_lp/README.md create mode 100644 plugin/send_notification_new_lp/SendNotificationToPublishLp.php create mode 100644 plugin/send_notification_new_lp/index.php create mode 100644 plugin/send_notification_new_lp/install.php create mode 100644 plugin/send_notification_new_lp/plugin.php create mode 100644 plugin/send_notification_new_lp/uninstall.php create mode 100644 plugin/sepe/CHANGELOG.md create mode 100644 plugin/sepe/README.md create mode 100644 plugin/sepe/admin.php create mode 100644 plugin/sepe/config.php create mode 100644 plugin/sepe/database.php create mode 100644 plugin/sepe/index.php create mode 100644 plugin/sepe/install.php create mode 100644 plugin/sepe/js/sepe.js create mode 100644 plugin/sepe/lang/english.php create mode 100644 plugin/sepe/lang/spanish.php create mode 100644 plugin/sepe/plugin.php create mode 100644 plugin/sepe/resources/button_delete.gif create mode 100644 plugin/sepe/resources/edit.png create mode 100644 plugin/sepe/resources/folder.png create mode 100644 plugin/sepe/resources/forms.png create mode 100644 plugin/sepe/resources/icon-delete.png create mode 100644 plugin/sepe/resources/icon-edit.png create mode 100644 plugin/sepe/resources/list.png create mode 100644 plugin/sepe/resources/options-lines.png create mode 100644 plugin/sepe/resources/plugin.css create mode 100644 plugin/sepe/resources/settings.png create mode 100644 plugin/sepe/src/configuration.php create mode 100644 plugin/sepe/src/formative-action-edit.php create mode 100644 plugin/sepe/src/formative-action.php create mode 100644 plugin/sepe/src/formative-actions-list.php create mode 100644 plugin/sepe/src/function.php create mode 100644 plugin/sepe/src/identification-data-edit.php create mode 100644 plugin/sepe/src/identification-data.php create mode 100644 plugin/sepe/src/index.php create mode 100644 plugin/sepe/src/index.sepe.php create mode 100644 plugin/sepe/src/participant-action-edit.php create mode 100644 plugin/sepe/src/participant-specialty-edit.php create mode 100644 plugin/sepe/src/sepe-administration-menu.php create mode 100644 plugin/sepe/src/sepe.lib.php create mode 100644 plugin/sepe/src/sepe_plugin.class.php create mode 100644 plugin/sepe/src/specialty-action-edit.php create mode 100644 plugin/sepe/src/specialty-classroom-edit.php create mode 100644 plugin/sepe/src/specialty-tutor-edit.php create mode 100644 plugin/sepe/src/specialty-tutorial-edit.php create mode 100644 plugin/sepe/src/wsse/soap-server-wsse.php create mode 100644 plugin/sepe/src/wsse/soap-wsa.php create mode 100644 plugin/sepe/src/wsse/soap-wsse.php create mode 100644 plugin/sepe/uninstall.php create mode 100644 plugin/sepe/update.php create mode 100644 plugin/sepe/view/configuration.tpl create mode 100644 plugin/sepe/view/formative-action-edit.tpl create mode 100644 plugin/sepe/view/formative-action.tpl create mode 100644 plugin/sepe/view/formative-actions-list.tpl create mode 100644 plugin/sepe/view/identification-data-edit.tpl create mode 100644 plugin/sepe/view/identification-data.tpl create mode 100644 plugin/sepe/view/participant-action-edit.tpl create mode 100644 plugin/sepe/view/participant-specialty-edit.tpl create mode 100644 plugin/sepe/view/sepe-administration-menu.tpl create mode 100644 plugin/sepe/view/specialty-action-edit.tpl create mode 100644 plugin/sepe/view/specialty-classroom-edit.tpl create mode 100644 plugin/sepe/view/specialty-tutor-edit.tpl create mode 100644 plugin/sepe/view/specialty-tutorial-edit.tpl create mode 100644 plugin/sepe/ws/ProveedorCentroTFWS.dist.wsdl create mode 100644 plugin/sepe/ws/Sepe.php create mode 100644 plugin/sepe/ws/service.php create mode 100644 plugin/share_buttons/README.md create mode 100644 plugin/share_buttons/index.php create mode 100644 plugin/share_buttons/plugin.php create mode 100644 plugin/show_regions/README.md create mode 100644 plugin/show_regions/index.php create mode 100644 plugin/show_regions/plugin.php create mode 100644 plugin/show_user_info/README.md create mode 100644 plugin/show_user_info/index.php create mode 100644 plugin/show_user_info/lang/english.php create mode 100644 plugin/show_user_info/lang/spanish.php create mode 100644 plugin/show_user_info/plugin.php create mode 100644 plugin/show_user_info/template.tpl create mode 100644 plugin/static/README.md create mode 100644 plugin/static/index.php create mode 100644 plugin/static/lang/english.php create mode 100644 plugin/static/lang/french.php create mode 100644 plugin/static/lang/spanish.php create mode 100644 plugin/static/lib/static_plugin.class.php create mode 100644 plugin/static/plugin.php create mode 100644 plugin/static/resources/static.css create mode 100644 plugin/studentfollowup/Entity/CarePost.php create mode 100644 plugin/studentfollowup/README.md create mode 100644 plugin/studentfollowup/StudentFollowUpPlugin.php create mode 100644 plugin/studentfollowup/demo_content.php create mode 100644 plugin/studentfollowup/index.php create mode 100644 plugin/studentfollowup/install.php create mode 100644 plugin/studentfollowup/lang/dutch.php create mode 100644 plugin/studentfollowup/lang/english.php create mode 100644 plugin/studentfollowup/my_students.php create mode 100644 plugin/studentfollowup/plugin.php create mode 100644 plugin/studentfollowup/post.php create mode 100644 plugin/studentfollowup/posts.php create mode 100644 plugin/studentfollowup/uninstall.php create mode 100644 plugin/studentfollowup/view/my_students.html.twig create mode 100644 plugin/studentfollowup/view/post.html.twig create mode 100644 plugin/studentfollowup/view/posts.html.twig create mode 100644 plugin/surveyexportcsv/README.md create mode 100644 plugin/surveyexportcsv/SurveyExportCsvPlugin.php create mode 100644 plugin/surveyexportcsv/export.php create mode 100644 plugin/surveyexportcsv/install.php create mode 100644 plugin/surveyexportcsv/lang/english.php create mode 100644 plugin/surveyexportcsv/lang/spanish.php create mode 100644 plugin/surveyexportcsv/plugin.php create mode 100644 plugin/surveyexportcsv/start.php create mode 100644 plugin/surveyexportcsv/uninstall.php create mode 100644 plugin/surveyexporttxt/README.md create mode 100644 plugin/surveyexporttxt/SurveyExportTxtPlugin.php create mode 100644 plugin/surveyexporttxt/export.php create mode 100644 plugin/surveyexporttxt/install.php create mode 100644 plugin/surveyexporttxt/lang/english.php create mode 100644 plugin/surveyexporttxt/lang/spanish.php create mode 100644 plugin/surveyexporttxt/plugin.php create mode 100644 plugin/surveyexporttxt/start.php create mode 100644 plugin/surveyexporttxt/uninstall.php create mode 100644 plugin/test2pdf/README.md create mode 100644 plugin/test2pdf/config.php create mode 100644 plugin/test2pdf/index.php create mode 100644 plugin/test2pdf/install.php create mode 100644 plugin/test2pdf/lang/english.php create mode 100644 plugin/test2pdf/lang/french.php create mode 100644 plugin/test2pdf/lang/spanish.php create mode 100644 plugin/test2pdf/plugin.php create mode 100644 plugin/test2pdf/resources/css/style.css create mode 100644 plugin/test2pdf/resources/img/22/test2pdf.png create mode 100644 plugin/test2pdf/resources/img/32/test2pdf.png create mode 100644 plugin/test2pdf/resources/img/32/test2pdf_na.png create mode 100644 plugin/test2pdf/resources/img/64/test2pdf.png create mode 100644 plugin/test2pdf/resources/img/64/test2pdf_na.png create mode 100644 plugin/test2pdf/src/download-pdf.php create mode 100644 plugin/test2pdf/src/index.php create mode 100644 plugin/test2pdf/src/index.test2pdf.php create mode 100644 plugin/test2pdf/src/test2pdf.lib.php create mode 100644 plugin/test2pdf/src/test2pdf_plugin.class.php create mode 100644 plugin/test2pdf/src/view-pdf.php create mode 100644 plugin/test2pdf/start.php create mode 100644 plugin/test2pdf/uninstall.php create mode 100644 plugin/test2pdf/view/view-pdf.tpl create mode 100644 plugin/toplinks/README.md create mode 100644 plugin/toplinks/admin.php create mode 100644 plugin/toplinks/index.php create mode 100644 plugin/toplinks/install.php create mode 100644 plugin/toplinks/lang/english.php create mode 100644 plugin/toplinks/lang/spanish.php create mode 100644 plugin/toplinks/plugin.php create mode 100644 plugin/toplinks/src/Entity/Repository/TopLinkRelToolRepository.php create mode 100644 plugin/toplinks/src/Entity/Repository/TopLinkRepository.php create mode 100644 plugin/toplinks/src/Entity/TopLink.php create mode 100644 plugin/toplinks/src/Entity/TopLinkRelTool.php create mode 100644 plugin/toplinks/src/Hook/TopLinksCreateCourseHookObserver.php create mode 100644 plugin/toplinks/src/LinkForm.php create mode 100644 plugin/toplinks/src/TopLinksPlugin.php create mode 100644 plugin/toplinks/start.php create mode 100644 plugin/toplinks/uninstall.php create mode 100644 plugin/tour/README.md create mode 100644 plugin/tour/ajax/save.ajax.php create mode 100644 plugin/tour/ajax/steps.ajax.php create mode 100644 plugin/tour/config.php create mode 100644 plugin/tour/config/tour.json create mode 100644 plugin/tour/index.php create mode 100644 plugin/tour/install.php create mode 100644 plugin/tour/intro.js/intro.min.js create mode 100644 plugin/tour/intro.js/introjs-nassim.css create mode 100644 plugin/tour/intro.js/introjs-nazanin.css create mode 100644 plugin/tour/intro.js/introjs-royal.css create mode 100644 plugin/tour/intro.js/introjs-rtl.min.css create mode 100644 plugin/tour/intro.js/introjs.min.css create mode 100644 plugin/tour/lang/brazilian.php create mode 100644 plugin/tour/lang/english.php create mode 100644 plugin/tour/lang/french.php create mode 100644 plugin/tour/lang/spanish.php create mode 100644 plugin/tour/plugin.php create mode 100644 plugin/tour/resources/fonts/lato-black.eot create mode 100644 plugin/tour/resources/fonts/lato-black.svg create mode 100644 plugin/tour/resources/fonts/lato-black.ttf create mode 100644 plugin/tour/resources/fonts/lato-black.woff create mode 100644 plugin/tour/resources/fonts/lato-bold.eot create mode 100644 plugin/tour/resources/fonts/lato-bold.svg create mode 100644 plugin/tour/resources/fonts/lato-bold.ttf create mode 100644 plugin/tour/resources/fonts/lato-bold.woff create mode 100644 plugin/tour/resources/fonts/lato-bolditalic.eot create mode 100644 plugin/tour/resources/fonts/lato-bolditalic.svg create mode 100644 plugin/tour/resources/fonts/lato-bolditalic.ttf create mode 100644 plugin/tour/resources/fonts/lato-bolditalic.woff create mode 100644 plugin/tour/resources/fonts/lato-italic.eot create mode 100644 plugin/tour/resources/fonts/lato-italic.svg create mode 100644 plugin/tour/resources/fonts/lato-italic.ttf create mode 100644 plugin/tour/resources/fonts/lato-italic.woff create mode 100644 plugin/tour/resources/fonts/lato-light.eot create mode 100644 plugin/tour/resources/fonts/lato-light.svg create mode 100644 plugin/tour/resources/fonts/lato-light.ttf create mode 100644 plugin/tour/resources/fonts/lato-light.woff create mode 100644 plugin/tour/resources/fonts/lato-regular.eot create mode 100644 plugin/tour/resources/fonts/lato-regular.svg create mode 100644 plugin/tour/resources/fonts/lato-regular.ttf create mode 100644 plugin/tour/resources/fonts/lato-regular.woff create mode 100644 plugin/tour/resources/tour-chamilo.png create mode 100644 plugin/tour/src/tour_plugin.class.php create mode 100644 plugin/tour/uninstall.php create mode 100644 plugin/tour/views/script.tpl create mode 100644 plugin/userremoteservice/Entity/UserRemoteService.php create mode 100644 plugin/userremoteservice/README.md create mode 100644 plugin/userremoteservice/admin.php create mode 100644 plugin/userremoteservice/config.php create mode 100644 plugin/userremoteservice/iframe.php create mode 100644 plugin/userremoteservice/index.php create mode 100644 plugin/userremoteservice/install.php create mode 100644 plugin/userremoteservice/lang/english.php create mode 100644 plugin/userremoteservice/lang/french.php create mode 100644 plugin/userremoteservice/plugin.php create mode 100644 plugin/userremoteservice/redirect.php create mode 100644 plugin/userremoteservice/src/user_remote_service_plugin.class.php create mode 100644 plugin/userremoteservice/uninstall.php create mode 100644 plugin/vchamilo/README.md create mode 100644 plugin/vchamilo/ajax/service.php create mode 100644 plugin/vchamilo/cli/bulkcreatenodes.php create mode 100644 plugin/vchamilo/cli/bulkdestroynodes.php create mode 100644 plugin/vchamilo/cli/clilib.php create mode 100644 plugin/vchamilo/cli/cron.php create mode 100644 plugin/vchamilo/cli/maincron.php create mode 100644 plugin/vchamilo/cli/nodelist-dist.csv create mode 100644 plugin/vchamilo/index.php create mode 100644 plugin/vchamilo/install.php create mode 100644 plugin/vchamilo/js/ajax.js create mode 100644 plugin/vchamilo/js/host_form.js create mode 100644 plugin/vchamilo/js/host_list.js create mode 100644 plugin/vchamilo/lang/english.php create mode 100644 plugin/vchamilo/lang/french.php create mode 100644 plugin/vchamilo/lang/spanish.php create mode 100644 plugin/vchamilo/lib/VChamiloPlugin.php create mode 100644 plugin/vchamilo/lib/Virtual.php create mode 100644 plugin/vchamilo/pix/ajax_waiter.gif create mode 100644 plugin/vchamilo/pix/icon.gif create mode 100644 plugin/vchamilo/pix/logo.png create mode 100644 plugin/vchamilo/plugin.php create mode 100644 plugin/vchamilo/template.tpl create mode 100644 plugin/vchamilo/templates/README.txt create mode 100644 plugin/vchamilo/uninstall.php create mode 100644 plugin/vchamilo/vcron.php create mode 100644 plugin/vchamilo/views/editinstance.php create mode 100644 plugin/vchamilo/views/editinstance_form.php create mode 100644 plugin/vchamilo/views/import.php create mode 100644 plugin/vchamilo/views/manage.controller.php create mode 100644 plugin/vchamilo/views/manage.php create mode 100644 plugin/vchamilo/views/manage.testcnx.php create mode 100644 plugin/vchamilo/views/manage.testdatapath.php create mode 100644 plugin/vchamilo/views/syncparams.controller.php create mode 100644 plugin/vchamilo/views/syncparams.php create mode 100644 plugin/vchamilo/views/upgrade.php create mode 100644 plugin/whispeakauth/Controller/AuthenticationController.php create mode 100644 plugin/whispeakauth/Controller/BaseController.php create mode 100644 plugin/whispeakauth/Controller/EnrollmentController.php create mode 100644 plugin/whispeakauth/Entity/LogEvent.php create mode 100644 plugin/whispeakauth/Entity/LogEventLp.php create mode 100644 plugin/whispeakauth/Entity/LogEventQuiz.php create mode 100644 plugin/whispeakauth/README.md create mode 100644 plugin/whispeakauth/Request/ApiRequest.php create mode 100644 plugin/whispeakauth/WhispeakAuthPlugin.php create mode 100644 plugin/whispeakauth/WhispeakConditionalLoginHook.php create mode 100644 plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php create mode 100644 plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php create mode 100644 plugin/whispeakauth/admin.php create mode 100644 plugin/whispeakauth/ajax/authentify_password.php create mode 100644 plugin/whispeakauth/ajax/record_audio.php create mode 100644 plugin/whispeakauth/assets/js/RecordAudio.js create mode 100644 plugin/whispeakauth/authentify.php create mode 100644 plugin/whispeakauth/authentify_password.php create mode 100644 plugin/whispeakauth/enrollment.php create mode 100644 plugin/whispeakauth/index.php create mode 100644 plugin/whispeakauth/install.php create mode 100644 plugin/whispeakauth/lang/english.php create mode 100644 plugin/whispeakauth/lang/french.php create mode 100644 plugin/whispeakauth/lang/spanish.php create mode 100644 plugin/whispeakauth/plugin.php create mode 100644 plugin/whispeakauth/revocation.php create mode 100644 plugin/whispeakauth/uninstall.php create mode 100644 plugin/whispeakauth/view/authentify_password.html.twig create mode 100644 plugin/whispeakauth/view/authentify_recorder.html.twig create mode 100644 plugin/whispeakauth/view/quiz_question.html.twig create mode 100644 plugin/whispeakauth/view/record_audio.html.twig create mode 100644 plugin/xapi/.htaccess create mode 100644 plugin/xapi/README.md create mode 100644 plugin/xapi/admin.php create mode 100644 plugin/xapi/assets/css/cmi5_launch.css create mode 100644 plugin/xapi/assets/js/cmi5_launch.js create mode 100644 plugin/xapi/cmi5/launch.php create mode 100644 plugin/xapi/cmi5/token.php create mode 100644 plugin/xapi/cmi5/view.php create mode 100644 plugin/xapi/cron/send_statements.php create mode 100644 plugin/xapi/install.php create mode 100644 plugin/xapi/lang/english.php create mode 100644 plugin/xapi/lang/french.php create mode 100644 plugin/xapi/lang/spanish.php create mode 100644 plugin/xapi/lrs.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/LICENSE create mode 100644 plugin/xapi/php-xapi/lrs-bundle/README.md create mode 100644 plugin/xapi/php-xapi/lrs-bundle/composer.json create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/Configuration.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/XApiLrsExtension.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/EventListener/AlternateRequestSyntaxListener.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/EventListener/ExceptionListener.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/EventListener/SerializerListener.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/EventListener/VersionListener.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/controller.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/doctrine.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/event_listener.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/factory.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/serializer.xml create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Response/AttachmentResponse.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Response/MultipartResponse.php create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/README.md create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/composer.json create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Actor.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Attachment.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Context.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Extensions.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Result.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Statement.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml create mode 100644 plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php create mode 100644 plugin/xapi/plugin.php create mode 100644 plugin/xapi/src/Entity/ActivityProfile.php create mode 100644 plugin/xapi/src/Entity/ActivityState.php create mode 100644 plugin/xapi/src/Entity/Cmi5Item.php create mode 100644 plugin/xapi/src/Entity/InternalLog.php create mode 100644 plugin/xapi/src/Entity/LrsAuth.php create mode 100644 plugin/xapi/src/Entity/Repository/ToolLaunchRepository.php create mode 100644 plugin/xapi/src/Entity/SharedStatement.php create mode 100644 plugin/xapi/src/Entity/ToolLaunch.php create mode 100644 plugin/xapi/src/Hook/XApiActivityHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiCreateCourseHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiLearningPathEndHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiLearningPathItemViewedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioCommentEditedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioCommentScoredHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioDownloadedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemAddedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemCommentedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemEditedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemHighlightedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemScoredHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiPortfolioItemViewedHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiQuizEndHookObserver.php create mode 100644 plugin/xapi/src/Hook/XApiQuizQuestionAnsweredHookObserver.php create mode 100644 plugin/xapi/src/Importer/PackageImporter.php create mode 100644 plugin/xapi/src/Importer/XmlPackageImporter.php create mode 100644 plugin/xapi/src/Importer/ZipPackageImporter.php create mode 100644 plugin/xapi/src/Lrs/AboutController.php create mode 100644 plugin/xapi/src/Lrs/ActivitiesProfileController.php create mode 100644 plugin/xapi/src/Lrs/ActivitiesStateController.php create mode 100644 plugin/xapi/src/Lrs/BaseController.php create mode 100644 plugin/xapi/src/Lrs/LrsRequest.php create mode 100644 plugin/xapi/src/Lrs/StatementsController.php create mode 100644 plugin/xapi/src/Lrs/Utils/InternalLogUtil.php create mode 100644 plugin/xapi/src/Parser/Cmi5Parser.php create mode 100644 plugin/xapi/src/Parser/PackageParser.php create mode 100644 plugin/xapi/src/Parser/TinCanParser.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/BaseActivity.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/Course.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/LearningPath.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/LearningPathItem.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/Portfolio.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/PortfolioCategory.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/PortfolioComment.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/PortfolioItem.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/Quiz.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/QuizQuestion.php create mode 100644 plugin/xapi/src/ToolExperience/Activity/Site.php create mode 100644 plugin/xapi/src/ToolExperience/Actor/BaseActor.php create mode 100644 plugin/xapi/src/ToolExperience/Actor/User.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/BaseStatement.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/LearningPathCompleted.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/LearningPathItemViewed.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioAttachmentsTrait.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioComment.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioCommentEdited.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioCommentScored.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioDownloaded.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItem.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemCommented.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemEdited.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemHighlighted.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemScored.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemShared.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/PortfolioItemViewed.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/QuizCompleted.php create mode 100644 plugin/xapi/src/ToolExperience/Statement/QuizQuestionAnswered.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Answered.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/BaseVerb.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Commented.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Completed.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Downloaded.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Edited.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Highlighted.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Replied.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Scored.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Shared.php create mode 100644 plugin/xapi/src/ToolExperience/Verb/Viewed.php create mode 100644 plugin/xapi/src/XApiPlugin.php create mode 100644 plugin/xapi/start.php create mode 100644 plugin/xapi/tincan/launch.php create mode 100644 plugin/xapi/tincan/stats.php create mode 100644 plugin/xapi/tincan/stats_attempts.ajax.php create mode 100644 plugin/xapi/tincan/stats_statements.ajax.php create mode 100644 plugin/xapi/tincan/view.php create mode 100644 plugin/xapi/tool_delete.php create mode 100644 plugin/xapi/tool_edit.php create mode 100644 plugin/xapi/tool_import.php create mode 100644 plugin/xapi/uninstall.php create mode 100644 plugin/xapi/views/cmi5_launch.twig create mode 100644 plugin/zoom/Entity/Meeting.php create mode 100644 plugin/zoom/Entity/MeetingActivity.php create mode 100644 plugin/zoom/Entity/Presenter.php create mode 100644 plugin/zoom/Entity/Recording.php create mode 100644 plugin/zoom/Entity/Registrant.php create mode 100644 plugin/zoom/Entity/Signature.php create mode 100644 plugin/zoom/Entity/Webinar.php create mode 100644 plugin/zoom/README.code.md create mode 100644 plugin/zoom/README.md create mode 100644 plugin/zoom/activity.php create mode 100644 plugin/zoom/attendance.php create mode 100644 plugin/zoom/calendar.ajax.php create mode 100644 plugin/zoom/calendar.php create mode 100644 plugin/zoom/config.php create mode 100644 plugin/zoom/endpoint.php create mode 100644 plugin/zoom/global.php create mode 100644 plugin/zoom/index.php create mode 100644 plugin/zoom/install.php create mode 100644 plugin/zoom/join_meeting.php create mode 100644 plugin/zoom/lang/english.php create mode 100644 plugin/zoom/lang/french.php create mode 100644 plugin/zoom/lang/spanish.php create mode 100644 plugin/zoom/lib/API/Api2RequestTrait.php create mode 100644 plugin/zoom/lib/API/BaseMeetingTrait.php create mode 100644 plugin/zoom/lib/API/Client.php create mode 100644 plugin/zoom/lib/API/CreatedRegistration.php create mode 100644 plugin/zoom/lib/API/CustomQuestion.php create mode 100644 plugin/zoom/lib/API/FollowUpUsers.php create mode 100644 plugin/zoom/lib/API/GlobalDialInNumber.php create mode 100644 plugin/zoom/lib/API/JWTClient.php create mode 100644 plugin/zoom/lib/API/JsonDeserializableTrait.php create mode 100644 plugin/zoom/lib/API/Meeting.php create mode 100644 plugin/zoom/lib/API/MeetingInfo.php create mode 100644 plugin/zoom/lib/API/MeetingInfoGet.php create mode 100644 plugin/zoom/lib/API/MeetingInstance.php create mode 100644 plugin/zoom/lib/API/MeetingInstances.php create mode 100644 plugin/zoom/lib/API/MeetingList.php create mode 100644 plugin/zoom/lib/API/MeetingListItem.php create mode 100644 plugin/zoom/lib/API/MeetingRegistrant.php create mode 100644 plugin/zoom/lib/API/MeetingRegistrantList.php create mode 100644 plugin/zoom/lib/API/MeetingRegistrantListItem.php create mode 100644 plugin/zoom/lib/API/MeetingSettings.php create mode 100644 plugin/zoom/lib/API/Ocurrence.php create mode 100644 plugin/zoom/lib/API/Pagination.php create mode 100644 plugin/zoom/lib/API/PaginationToken.php create mode 100644 plugin/zoom/lib/API/ParticipantList.php create mode 100644 plugin/zoom/lib/API/ParticipantListItem.php create mode 100644 plugin/zoom/lib/API/PastMeeting.php create mode 100644 plugin/zoom/lib/API/QuestionAndAnswer.php create mode 100644 plugin/zoom/lib/API/RecordingFile.php create mode 100644 plugin/zoom/lib/API/RecordingList.php create mode 100644 plugin/zoom/lib/API/RecordingMeeting.php create mode 100644 plugin/zoom/lib/API/RegistrantSchema.php create mode 100644 plugin/zoom/lib/API/ServerToServerOAuthClient.php create mode 100644 plugin/zoom/lib/API/TrackingField.php create mode 100644 plugin/zoom/lib/API/WebinarRegistrantSchema.php create mode 100644 plugin/zoom/lib/API/WebinarSchema.php create mode 100644 plugin/zoom/lib/API/WebinarSettings.php create mode 100644 plugin/zoom/lib/MeetingRepository.php create mode 100644 plugin/zoom/lib/RecordingRepository.php create mode 100644 plugin/zoom/lib/RegistrantRepository.php create mode 100644 plugin/zoom/lib/ZoomPlugin.php create mode 100644 plugin/zoom/meeting.ajax.php create mode 100644 plugin/zoom/meeting.php create mode 100644 plugin/zoom/meetings.php create mode 100644 plugin/zoom/plugin.php create mode 100644 plugin/zoom/resources/img/22/zoom_meet.png create mode 100644 plugin/zoom/resources/img/22/zoom_meet_na.png create mode 100644 plugin/zoom/resources/img/32/zoom_meet.png create mode 100644 plugin/zoom/resources/img/32/zoom_meet_na.png create mode 100644 plugin/zoom/resources/img/64/zoom_meet.png create mode 100644 plugin/zoom/resources/img/64/zoom_meet_na.png create mode 100644 plugin/zoom/resources/img/svg/zoommeet.svg create mode 100644 plugin/zoom/start.php create mode 100644 plugin/zoom/subscription.php create mode 100644 plugin/zoom/uninstall.php create mode 100644 plugin/zoom/view/activity.tpl create mode 100644 plugin/zoom/view/calendar.tpl create mode 100644 plugin/zoom/view/join.tpl create mode 100644 plugin/zoom/view/meeting.tpl create mode 100644 plugin/zoom/view/meeting_details.tpl create mode 100644 plugin/zoom/view/meetings.tpl create mode 100644 plugin/zoom/view/start.tpl create mode 100644 plugin/zoom/view/subscription.tpl diff --git a/plugin/add_cas_login_button/README.md b/plugin/add_cas_login_button/README.md new file mode 100644 index 000000000..52071b311 --- /dev/null +++ b/plugin/add_cas_login_button/README.md @@ -0,0 +1,11 @@ +Add CAS login button plugin +====== +This plugin adds a button to allow users to login to Chamilo through CAS authentication. + +In order for the plugin to work, you'll have to: + +* enable your CAS connection to display this button +* configure your CAS connection to have the button work +* go to Administration > Configuration settings > CAS and follow the instructions + +This plugin has been made to be added in the login_top region, but you can put it wherever you want. \ No newline at end of file diff --git a/plugin/add_cas_login_button/css.css b/plugin/add_cas_login_button/css.css new file mode 100644 index 000000000..60689dd4b --- /dev/null +++ b/plugin/add_cas_login_button/css.css @@ -0,0 +1,12 @@ +.cas_plugin_image { + float:left; + height:50px; + margin: 0px 5px 5px 0px; +} +.cas_plugin_comm { + font-style:italic; +} +.cas_plugin_clear { + clear:both; + height:1px; +} diff --git a/plugin/add_cas_login_button/index.php b/plugin/add_cas_login_button/index.php new file mode 100644 index 000000000..dc10a6432 --- /dev/null +++ b/plugin/add_cas_login_button/index.php @@ -0,0 +1,29 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +//the plugin title +$plugin_info['title'] = 'Add a button to login using CAS'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If CAS is activated, this plugin add a text and a button on the login page to login with CAS. Configure plugin to add title, comment and logo."; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Hubert Borderiou'; + +//the plugin configuration +$form = new FormValidator('add_cas_button_form'); +$form->addElement('text', 'cas_button_label', 'CAS connexion title', ''); +$form->addElement('text', 'cas_button_comment', 'CAS connexion description', ''); +$form->addElement('text', 'cas_image_url', 'Logo URL if any (image, 50px height)'); +$form->addButtonSave(get_lang('Save'), 'submit_button'); +//get default value for form +$tab_default_add_cas_login_button_cas_button_label = api_get_setting('add_cas_login_button_cas_button_label'); +$tab_default_add_cas_login_button_cas_button_comment = api_get_setting('add_cas_login_button_cas_button_comment'); +$tab_default_add_cas_login_button_cas_image_url = api_get_setting('add_cas_login_button_cas_image_url'); +$defaults = []; +if ($tab_default_add_cas_login_button_cas_button_label) { + $defaults['cas_button_label'] = $tab_default_add_cas_login_button_cas_button_label['add_cas_login_button']; +} +if ($tab_default_add_cas_login_button_cas_button_comment) { + $defaults['cas_button_comment'] = $tab_default_add_cas_login_button_cas_button_comment['add_cas_login_button']; +} +if ($tab_default_add_cas_login_button_cas_image_url) { + $defaults['cas_image_url'] = $tab_default_add_cas_login_button_cas_image_url['add_cas_login_button']; +} +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +//set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/add_cas_login_button/template.tpl b/plugin/add_cas_login_button/template.tpl new file mode 100644 index 000000000..45c784b8f --- /dev/null +++ b/plugin/add_cas_login_button/template.tpl @@ -0,0 +1,22 @@ +{% if add_cas_login_button.show_message %} + +
+ {% if add_cas_login_button.url_label %} + + {% endif %} +

{{ add_cas_login_button.button_label }}

+ {% if add_cas_login_button.url_label %} +
 
+ {% endif %} +
{{ add_cas_login_button.comm_label }}
+ {% if add_cas_login_button.cas_activated %} + {% if add_cas_login_button.cas_configured %} + {{ add_cas_login_button.form }} + {% else %} + CAS isn't configured. Go to Admin > Configuration > CAS.
+ {% endif %} + {% else %} + CAS isn't activated. Go to Admin > Configuration > CAS.
+ {% endif %} +
+{% endif %} diff --git a/plugin/add_cas_logout_button/README.md b/plugin/add_cas_logout_button/README.md new file mode 100644 index 000000000..c5ca74393 --- /dev/null +++ b/plugin/add_cas_logout_button/README.md @@ -0,0 +1,12 @@ +Add CAS logout button plugin +=== + +This plugin adds a button to allow users to logout from their CAS session. + +In order for the plugin to work, you'll have to: + +* enable your CAS connection to display this button. +* configure your CAS connection to have the button works. +* go to Administration > Configuration settings > CAS and follow the instructions. + +This plugin has been made to be added in the login_top region, but you can put it wherever you want. \ No newline at end of file diff --git a/plugin/add_cas_logout_button/css.css b/plugin/add_cas_logout_button/css.css new file mode 100644 index 000000000..60689dd4b --- /dev/null +++ b/plugin/add_cas_logout_button/css.css @@ -0,0 +1,12 @@ +.cas_plugin_image { + float:left; + height:50px; + margin: 0px 5px 5px 0px; +} +.cas_plugin_comm { + font-style:italic; +} +.cas_plugin_clear { + clear:both; + height:1px; +} diff --git a/plugin/add_cas_logout_button/index.php b/plugin/add_cas_logout_button/index.php new file mode 100644 index 000000000..668f60907 --- /dev/null +++ b/plugin/add_cas_logout_button/index.php @@ -0,0 +1,26 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +//the plugin title +$plugin_info['title'] = 'Add a button to logout from CAS'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If CAS is activated, this plugin add a text and a button on the user page to logout from a CAS session. Configure plugin to add title, comment and logo."; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Hubert Borderiou'; +//the plugin configuration +$form = new FormValidator('add_cas_button_form'); +$form->addElement('text', 'cas_logout_label', 'CAS logout title', ''); +$form->addElement('text', 'cas_logout_comment', 'CAS logout description', ''); +$form->addElement('text', 'cas_logout_image_url', 'Logo URL if any (image, 50px height)'); +$form->addButtonSave(get_lang('Save'), 'submit_button'); +//get default value for form +$defaults = []; +$tab_default_add_cas_logout_button_cas_logout_label = api_get_setting('add_cas_logout_button_cas_logout_label'); +$tab_default_add_cas_logout_button_cas_logout_comment = api_get_setting('add_cas_logout_button_cas_logout_comment'); +$tab_default_add_cas_logout_button_cas_logout_image_url = api_get_setting('add_cas_logout_button_cas_logout_image_url'); +if ($tab_default_add_cas_logout_button_cas_logout_label) { + $defaults['cas_logout_label'] = $tab_default_add_cas_logout_button_cas_logout_label['add_cas_logout_button']; +} +if ($tab_default_add_cas_logout_button_cas_logout_comment) { + $defaults['cas_logout_comment'] = $tab_default_add_cas_logout_button_cas_logout_comment['add_cas_logout_button']; +} + +if ($tab_default_add_cas_logout_button_cas_logout_image_url) { + $defaults['cas_logout_image_url'] = $tab_default_add_cas_logout_button_cas_logout_image_url['add_cas_logout_button']; +} + +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +// Set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/add_cas_logout_button/template.tpl b/plugin/add_cas_logout_button/template.tpl new file mode 100644 index 000000000..44ad94991 --- /dev/null +++ b/plugin/add_cas_logout_button/template.tpl @@ -0,0 +1,15 @@ +{% if add_cas_logout_button.show_message %} + +
+ {% if add_cas_logout_button.logout_image_url %} + + {% endif %} +

{{add_cas_logout_button.logout_label}}

+ {% if add_cas_logout_button.logout_image_url %} +
 
+ {% endif %} +
{{add_cas_logout_button.logout_comment}}
+ {{ add_cas_logout_button.form }} + +
+{% endif %} diff --git a/plugin/add_facebook_login_button/README.md b/plugin/add_facebook_login_button/README.md new file mode 100644 index 000000000..dfd998095 --- /dev/null +++ b/plugin/add_facebook_login_button/README.md @@ -0,0 +1,16 @@ +Add Facebook login button plugin +=== + +This plugin adds a button to allow users to log into Chamilo through their Facebook account. + +To display this button on your portal, you have to: + +* enable the Facebook authentication setting and configure it +* enable and configure Facebook authentication on your Chamilo platform: go to Administration > Configuration settings > Facebook +* add the App ID and the Secret Key provided by Facebook inside the app/config/auth.conf.php file +* set the following line in your app/config/configuration.php +``` +$_configuration['facebook_auth'] = 1; +``` + +This plugin has been developed to be added to the login_top or login_bottom region in Chamilo, but you can put it in whichever region you want. diff --git a/plugin/add_facebook_login_button/css.css b/plugin/add_facebook_login_button/css.css new file mode 100644 index 000000000..945a00d0c --- /dev/null +++ b/plugin/add_facebook_login_button/css.css @@ -0,0 +1,12 @@ + .cas_plugin_image { + float:left; + height:50px; + margin: 0px 5px 5px 0px; + } + .cas_plugin_comm { + font-style:italic; + } + .cas_plugin_clear { + clear:both; + height:1px; + } diff --git a/plugin/add_facebook_login_button/index.php b/plugin/add_facebook_login_button/index.php new file mode 100644 index 000000000..ca8d52fce --- /dev/null +++ b/plugin/add_facebook_login_button/index.php @@ -0,0 +1,17 @@ + + */ +/** + * Plugin details. + */ + +//the plugin title +$plugin_info['title'] = 'Add a button to login using a FACEBOOK account'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If Facebook authentication is enabled, this plugin adds a button Facebook Connexion on the login page. Configure the plugin to add a title, a comment and a logo. Should be placed in login_top region"; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Konrad Banasiak, Hubert Borderiou'; +//the plugin configuration +$form = new FormValidator('add_facebook_button_form'); +$form->addElement( + 'text', + 'facebook_button_url', + 'Facebook connexion image URL', + '' +); +$form->addButtonSave(get_lang('Save'), 'submit_button'); +//get default value for form +$tab_default_add_facebook_login_button_facebook_button_url = api_get_setting( + 'add_facebook_login_button_facebook_button_url' +); + +$defaults = []; + +if ($tab_default_add_facebook_login_button_facebook_button_url) { + $defaults['facebook_button_url'] = $tab_default_add_facebook_login_button_facebook_button_url['add_facebook_login_button']; +} + +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +// Set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/add_facebook_login_button/template.tpl b/plugin/add_facebook_login_button/template.tpl new file mode 100644 index 000000000..f9165173d --- /dev/null +++ b/plugin/add_facebook_login_button/template.tpl @@ -0,0 +1,14 @@ +{% if add_facebook_login_button.show_message %} +
+ {% if add_facebook_login_button.facebook_button_url %} + + {{ 'FacebookMainActivateTitle'|get_lang }} + + {% else %} + + {{ 'FacebookMainActivateTitle'|get_lang }} + + {% endif %} +
+{% endif %} diff --git a/plugin/add_shibboleth_login_button/README.md b/plugin/add_shibboleth_login_button/README.md new file mode 100644 index 000000000..ef8899b24 --- /dev/null +++ b/plugin/add_shibboleth_login_button/README.md @@ -0,0 +1,10 @@ +Add Shibboleth login button plugin +=== + +This plugin adds a button to allow users to login to Chamilo through Shibboleth authentication. + +You have to configure your Shibboleth connexion before using this plugin. + +To activate and configure Shibboleth for your Chamilo platform, go to Administration > Configuration settings > Shibboleth + +This plugin has been created to be added in the login_top region, but you can put it wherever you want. \ No newline at end of file diff --git a/plugin/add_shibboleth_login_button/css.css b/plugin/add_shibboleth_login_button/css.css new file mode 100644 index 000000000..a79de810c --- /dev/null +++ b/plugin/add_shibboleth_login_button/css.css @@ -0,0 +1,12 @@ +.shibboleth_plugin_image { + float:left; + height:50px; + margin: 0px 5px 5px 0px; +} +.shibboleth_plugin_comm { + font-style:italic; +} +.shibboleth_plugin_clear { + clear:both; + height:1px; +} diff --git a/plugin/add_shibboleth_login_button/index.php b/plugin/add_shibboleth_login_button/index.php new file mode 100644 index 000000000..8b2d719dd --- /dev/null +++ b/plugin/add_shibboleth_login_button/index.php @@ -0,0 +1,22 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +//the plugin title +$plugin_info['title'] = 'Add a button to login using Shibboleth'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If Shibboleth is configured, this plugin add a text and a button on the login page to login with Shibboleth. Configure plugin to add title, comment and logo."; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Hubert Borderiou'; + +//the plugin configuration +$form = new FormValidator('add_shibboleth_button_form'); +$form->addElement( + 'text', + 'shibboleth_button_label', + 'shibboleth connexion title', + '' +); +$form->addElement( + 'text', + 'shibboleth_button_comment', + 'shibboleth connexion description', + '' +); +$form->addElement( + 'text', + 'shibboleth_image_url', + 'Logo URL if any (image, 50px height)' +); +$form->addButtonSave(get_lang('Save'), 'submit_button'); +//get default value for form +$tab_default_add_shibboleth_login_button_shibboleth_button_label = api_get_setting( + 'add_shibboleth_login_button_shibboleth_button_label' +); +$tab_default_add_shibboleth_login_button_shibboleth_button_comment = api_get_setting( + 'add_shibboleth_login_button_shibboleth_button_comment' +); +$tab_default_add_shibboleth_login_button_shibboleth_image_url = api_get_setting( + 'add_shibboleth_login_button_shibboleth_image_url' +); +$defaults = []; +if ($tab_default_add_shibboleth_login_button_shibboleth_button_label) { + $defaults['shibboleth_button_label'] = $tab_default_add_shibboleth_login_button_shibboleth_button_label['add_shibboleth_login_button']; +} + +if ($tab_default_add_shibboleth_login_button_shibboleth_button_comment) { + $defaults['shibboleth_button_comment'] = $tab_default_add_shibboleth_login_button_shibboleth_button_comment['add_shibboleth_login_button']; +} + +if ($tab_default_add_shibboleth_login_button_shibboleth_image_url) { + $defaults['shibboleth_image_url'] = $tab_default_add_shibboleth_login_button_shibboleth_image_url['add_shibboleth_login_button']; +} + +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +//set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/add_shibboleth_login_button/template.tpl b/plugin/add_shibboleth_login_button/template.tpl new file mode 100644 index 000000000..e42d26d6a --- /dev/null +++ b/plugin/add_shibboleth_login_button/template.tpl @@ -0,0 +1,15 @@ + +{% if add_shibboleth_login_button.show_message %} + +
+ {% if add_shibboleth_login_button.url_label %} + + {% endif %} +

{{ add_shibboleth_login_button.button_label }}

+ {% if add_shibboleth_login_button.url_label %} +
 
+ {% endif %} +
{{ add_shibboleth_login_button.comm_label }}
+ +
+{% endif %} diff --git a/plugin/advanced_subscription/README.md b/plugin/advanced_subscription/README.md new file mode 100644 index 000000000..0036e183d --- /dev/null +++ b/plugin/advanced_subscription/README.md @@ -0,0 +1,59 @@ +Advanced subscription plugin for Chamilo LMS +======================================= +Plugin to manage the registration queue and communication to sessions +from an external website creating a queue to control session subscription +and sending emails to approve student subscription requests + +# Requirements +Chamilo LMS 1.10.0 or greater + +# Settings + +These settings have to be configured in the Configuration screen for the plugin + +Parameters | Description +------------- |------------- +Webservice url | Url to external website to get user profile (SOAP) +Induction requirement | Checkbox to enable induction as requirement +Courses count limit | Number of times a student is allowed at most to course by year +Yearly hours limit | Teaching hours a student is allowed at most to course by year +Yearly cost unit converter | The cost of a taxation unit value (TUV) +Yearly cost limit | Number of TUV student courses is allowed at most to cost by year +Year start date | Date (dd/mm) when the year limit is renewed +Minimum percentage profile | Minimum percentage required from external website profile + +# Hooks + +This plugin uses the following hooks (defined since Chamilo LMS 1.10.0): + +* HookAdminBlock +* HookWSRegistration +* HookNotificationContent +* HookNotificationTitle + + +# Web services + +This plugin also enables new webservices that can be used from registration.soap.php + +* HookAdvancedSubscription..WSSessionListInCategory +* HookAdvancedSubscription..WSSessionGetDetailsByUser +* HookAdvancedSubscription..WSListSessionsDetailsByCategory + +See `/plugin/advanced_subscription/src/HookAdvancedSubscription.php` to check Web services inputs and outputs + +# How does this plugin works? + +After install, fill the required parameters (described above) +Use web services to communicate course session inscription from external website +This allows students to search course sessions and subscribe if they match +the requirements. + +The normal process is: +* Student searches course session +* Student reads session info depending student data +* Student requests to be subscribed +* A confirmation email is sent to student +* An authorization email is sent to student's superior (STUDENT BOSS role) or admins (when there is no superior) who will accept or reject the student request +* When the superior accepts or rejects, an email will be sent to the student and superior (or admin), respectively +* To complete the subscription, the request must be validated and accepted by an admin \ No newline at end of file diff --git a/plugin/advanced_subscription/ajax/advanced_subscription.ajax.php b/plugin/advanced_subscription/ajax/advanced_subscription.ajax.php new file mode 100644 index 000000000..3af22fee3 --- /dev/null +++ b/plugin/advanced_subscription/ajax/advanced_subscription.ajax.php @@ -0,0 +1,350 @@ + + * + * @package chamilo.plugin.advanced_subscription + */ +/** + * Init. + */ +require_once __DIR__.'/../config.php'; + +$plugin = AdvancedSubscriptionPlugin::create(); +// Get validation hash +$hash = Security::remove_XSS($_REQUEST['v']); +// Get data from request (GET or POST) +$data['action'] = Security::remove_XSS($_REQUEST['a']); +$data['sessionId'] = intval($_REQUEST['s']); +$data['currentUserId'] = intval($_REQUEST['current_user_id']); +$data['studentUserId'] = intval($_REQUEST['u']); +$data['queueId'] = intval($_REQUEST['q']); +$data['newStatus'] = intval($_REQUEST['e']); +// Student always is connected +// $data['is_connected'] = isset($_REQUEST['is_connected']) ? boolval($_REQUEST['is_connected']) : false; +$data['is_connected'] = true; +$data['profile_completed'] = isset($_REQUEST['profile_completed']) ? floatval($_REQUEST['profile_completed']) : 0; +$data['accept_terms'] = isset($_REQUEST['accept_terms']) ? intval($_REQUEST['accept_terms']) : 0; +$data['courseId'] = isset($_REQUEST['c']) ? intval($_REQUEST['c']) : 0; +// Init result array +$result = ['error' => true, 'errorMessage' => get_lang('ThereWasAnError')]; +$showJSON = true; +// Check if data is valid or is for start subscription +$verified = $plugin->checkHash($data, $hash) || $data['action'] == 'subscribe'; +if ($verified) { + switch ($data['action']) { + case 'check': // Check minimum requirements + try { + $res = AdvancedSubscriptionPlugin::create()->isAllowedToDoRequest($data['studentUserId'], $data); + if ($res) { + $result['error'] = false; + $result['errorMessage'] = 'No error'; + $result['pass'] = true; + } else { + $result['errorMessage'] = 'User can not be subscribed'; + $result['pass'] = false; + } + } catch (\Exception $e) { + $result['errorMessage'] = $e->getMessage(); + } + break; + case 'subscribe': // Subscription + // Start subscription to queue + $res = AdvancedSubscriptionPlugin::create()->startSubscription( + $data['studentUserId'], + $data['sessionId'], + $data + ); + // Check if queue subscription was successful + if ($res === true) { + $legalEnabled = api_get_plugin_setting('courselegal', 'tool_enable'); + if ($legalEnabled) { + // Save terms confirmation + CourseLegalPlugin::create()->saveUserLegal( + $data['studentUserId'], + $data['courseId'], + $data['sessionId'], + false + ); + } + // Prepare data + // Get session data + // Assign variables + $fieldsArray = [ + 'description', + 'target', + 'mode', + 'publication_end_date', + 'recommended_number_of_participants', + ]; + $sessionArray = api_get_session_info($data['sessionId']); + $extraSession = new ExtraFieldValue('session'); + $extraField = new ExtraField('session'); + // Get session fields + $fieldList = $extraField->get_all([ + 'variable IN ( ?, ?, ?, ?, ?)' => $fieldsArray, + ]); + // Index session fields + foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; + } + + $mergedArray = array_merge([$data['sessionId']], array_keys($fields)); + $sessionFieldValueList = $extraSession->get_all( + [ + 'item_id = ? field_id IN ( ?, ?, ?, ?, ?, ?, ? )' => $mergedArray, + ] + ); + foreach ($sessionFieldValueList as $sessionFieldValue) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } + } + // Get student data + $studentArray = api_get_user_info($data['studentUserId']); + $studentArray['picture'] = $studentArray['avatar']; + + // Get superior data if exist + $superiorId = UserManager::getFirstStudentBoss($data['studentUserId']); + if (!empty($superiorId)) { + $superiorArray = api_get_user_info($superiorId); + } else { + $superiorArray = null; + } + // Get admin data + $adminsArray = UserManager::get_all_administrators(); + $isWesternNameOrder = api_is_western_name_order(); + foreach ($adminsArray as &$admin) { + $admin['complete_name'] = $isWesternNameOrder ? + $admin['firstname'].', '.$admin['lastname'] : $admin['lastname'].', '.$admin['firstname'] + ; + } + unset($admin); + // Set data + $data['action'] = 'confirm'; + $data['student'] = $studentArray; + $data['superior'] = $superiorArray; + $data['admins'] = $adminsArray; + $data['session'] = $sessionArray; + $data['signature'] = api_get_setting('Institution'); + + // Check if student boss exists + if (empty($superiorId)) { + // Student boss does not exist + // Update status to accepted by boss + $res = $plugin->updateQueueStatus($data, ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED); + if (!empty($res)) { + // Prepare admin url + $data['admin_view_url'] = api_get_path(WEB_PLUGIN_PATH). + 'advanced_subscription/src/admin_view.php?s='.$data['sessionId']; + // Send mails + $result['mailIds'] = $plugin->sendMail( + $data, + ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST_NO_BOSS + ); + // Check if mails were sent + if (!empty($result['mailIds'])) { + $result['error'] = false; + $result['errorMessage'] = 'No error'; + $result['pass'] = true; + // Check if exist an email to render + if (isset($result['mailIds']['render'])) { + // Render mail + $url = $plugin->getRenderMailUrl(['queueId' => $result['mailIds']['render']]); + header('Location: '.$url); + exit; + } + } + } + } else { + // Student boss does exist + // Get url to be accepted by boss + $data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED; + $data['student']['acceptUrl'] = $plugin->getQueueUrl($data); + // Get url to be rejected by boss + $data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED; + $data['student']['rejectUrl'] = $plugin->getQueueUrl($data); + // Send mails + $result['mailIds'] = $plugin->sendMail($data, ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST); + // Check if mails were sent + if (!empty($result['mailIds'])) { + $result['error'] = false; + $result['errorMessage'] = 'No error'; + $result['pass'] = true; + // Check if exist an email to render + if (isset($result['mailIds']['render'])) { + // Render mail + $url = $plugin->getRenderMailUrl(['queueId' => $result['mailIds']['render']]); + header('Location: '.$url); + exit; + } + } + } + } else { + $lastMessageId = $plugin->getLastMessageId($data['studentUserId'], $data['sessionId']); + if ($lastMessageId !== false) { + // Render mail + $url = $plugin->getRenderMailUrl(['queueId' => $lastMessageId]); + header('Location: '.$url); + exit; + } else { + if (is_string($res)) { + $result['errorMessage'] = $res; + } else { + $result['errorMessage'] = 'User can not be subscribed'; + } + $result['pass'] = false; + $url = $plugin->getTermsUrl($data, ADVANCED_SUBSCRIPTION_TERMS_MODE_FINAL); + header('Location: '.$url); + exit; + } + } + + break; + case 'confirm': + // Check if new status is set + if (isset($data['newStatus'])) { + if ($data['newStatus'] === ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED) { + try { + $isAllowToDoRequest = $plugin->isAllowedToDoRequest($data['studentUserId'], $data); + } catch (Exception $ex) { + $messageTemplate = new Template(null, false, false); + $messageTemplate->assign( + 'content', + Display::return_message($ex->getMessage(), 'error', false) + ); + $messageTemplate->display_no_layout_template(); + $showJSON = false; + break; + } + } + + // Update queue status + $res = $plugin->updateQueueStatus($data, $data['newStatus']); + if ($res === true) { + // Prepare data + // Prepare session data + $fieldsArray = [ + 'description', + 'target', + 'mode', + 'publication_end_date', + 'recommended_number_of_participants', + ]; + $sessionArray = api_get_session_info($data['sessionId']); + $extraSession = new ExtraFieldValue('session'); + $extraField = new ExtraField('session'); + // Get session fields + $fieldList = $extraField->get_all([ + 'variable IN ( ?, ?, ?, ?, ?)' => $fieldsArray, + ]); + // Index session fields + foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; + } + + $mergedArray = array_merge([$data['sessionId']], array_keys($fields)); + $sessionFieldValueList = $extraSession->get_all( + ['session_id = ? field_id IN ( ?, ?, ?, ?, ?, ?, ? )' => $mergedArray] + ); + foreach ($sessionFieldValueList as $sessionFieldValue) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } + } + // Prepare student data + $studentArray = api_get_user_info($data['studentUserId']); + $studentArray['picture'] = $studentArray['avatar']; + // Prepare superior data + $superiorId = UserManager::getFirstStudentBoss($data['studentUserId']); + if (!empty($superiorId)) { + $superiorArray = api_get_user_info($superiorId); + } else { + $superiorArray = null; + } + // Prepare admin data + $adminsArray = UserManager::get_all_administrators(); + $isWesternNameOrder = api_is_western_name_order(); + foreach ($adminsArray as &$admin) { + $admin['complete_name'] = $isWesternNameOrder ? + $admin['firstname'].', '.$admin['lastname'] : $admin['lastname'].', '.$admin['firstname'] + ; + } + unset($admin); + // Set data + $data['student'] = $studentArray; + $data['superior'] = $superiorArray; + $data['admins'] = $adminsArray; + $data['session'] = $sessionArray; + $data['signature'] = api_get_setting('Institution'); + $data['admin_view_url'] = api_get_path(WEB_PLUGIN_PATH) + .'advanced_subscription/src/admin_view.php?s='.$data['sessionId']; + // Check if exist and action in data + if (empty($data['mailAction'])) { + // set action in data by new status + switch ($data['newStatus']) { + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED: + $data['mailAction'] = ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_APPROVE; + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED: + $data['mailAction'] = ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_DISAPPROVE; + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED: + $data['mailAction'] = ADVANCED_SUBSCRIPTION_ACTION_ADMIN_APPROVE; + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED: + $data['mailAction'] = ADVANCED_SUBSCRIPTION_ACTION_ADMIN_DISAPPROVE; + break; + default: + break; + } + } + + // Student Session inscription + if ($data['newStatus'] == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED) { + SessionManager::subscribeUsersToSession( + $data['sessionId'], + [$data['studentUserId']], + null, + false + ); + } + + // Send mails + $result['mailIds'] = $plugin->sendMail($data, $data['mailAction']); + // Check if mails were sent + if (!empty($result['mailIds'])) { + $result['error'] = false; + $result['errorMessage'] = 'User has been processed'; + // Check if exist mail to render + if (isset($result['mailIds']['render'])) { + // Render mail + $url = $plugin->getRenderMailUrl(['queueId' => $result['mailIds']['render']]); + header('Location: '.$url); + exit; + } + } + } else { + $result['errorMessage'] = 'User queue can not be updated'; + } + } + break; + default: + $result['errorMessage'] = 'This action does not exist!'; + } +} + +if ($showJSON) { + // Echo result as json + echo json_encode($result); +} diff --git a/plugin/advanced_subscription/config.php b/plugin/advanced_subscription/config.php new file mode 100644 index 000000000..805d5b4d5 --- /dev/null +++ b/plugin/advanced_subscription/config.php @@ -0,0 +1,35 @@ + + * + * @package chamilo.plugin.advanced_subscription + */ +define('TABLE_ADVANCED_SUBSCRIPTION_QUEUE', 'plugin_advanced_subscription_queue'); + +define('ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST', 0); +define('ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_APPROVE', 1); +define('ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_DISAPPROVE', 2); +define('ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_SELECT', 3); +define('ADVANCED_SUBSCRIPTION_ACTION_ADMIN_APPROVE', 4); +define('ADVANCED_SUBSCRIPTION_ACTION_ADMIN_DISAPPROVE', 5); +define('ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST_NO_BOSS', 6); +define('ADVANCED_SUBSCRIPTION_ACTION_REMINDER_STUDENT', 7); +define('ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR', 8); +define('ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR_MAX', 9); +define('ADVANCED_SUBSCRIPTION_ACTION_REMINDER_ADMIN', 10); + +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE', -1); +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START', 0); +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED', 1); +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED', 2); +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED', 3); +define('ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED', 10); + +define('ADVANCED_SUBSCRIPTION_TERMS_MODE_POPUP', 0); +define('ADVANCED_SUBSCRIPTION_TERMS_MODE_REJECT', 1); +define('ADVANCED_SUBSCRIPTION_TERMS_MODE_FINAL', 2); + +require_once __DIR__.'/../../main/inc/global.inc.php'; diff --git a/plugin/advanced_subscription/cron/notify_by_mail.php b/plugin/advanced_subscription/cron/notify_by_mail.php new file mode 100644 index 000000000..3446aaa46 --- /dev/null +++ b/plugin/advanced_subscription/cron/notify_by_mail.php @@ -0,0 +1,153 @@ + [ + 's.access_start_date >= ? AND uu.relation_type = ? AND asq.updated_at <= ?' => [ + $now, + USER_RELATION_TYPE_BOSS, + $weekAgo, + ], + ], + 'order' => 's.id', +]; + +$queueList = Database::select($columns, $joinTables, $conditions); + +/** + * Remind students. + */ +$sessionInfoList = []; +foreach ($queueList as $queueItem) { + if (!isset($sessionInfoList[$queueItem['session_id']])) { + $sessionInfoList[$queueItem['session_id']] = api_get_session_info($queueItem['session_id']); + } +} + +foreach ($queueList as $queueItem) { + switch ($queueItem['status']) { + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START: + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED: + $data = ['sessionId' => $queueItem['session_id']]; + $data['session'] = api_get_session_info($queueItem['session_id']); + $data['student'] = api_get_user_info($queueItem['student_id']); + $plugin->sendMail($data, ADVANCED_SUBSCRIPTION_ACTION_REMINDER_STUDENT); + break; + default: + break; + } +} + +/** + * Remind superiors. + */ +// Get recommended number of participants +$sessionRecommendedNumber = []; +foreach ($queueList as $queueItem) { + $row = + $sessionExtraFieldValue->get_values_by_handler_and_field_variable( + $queueItem['session_id'], + 'recommended_number_of_participants' + ); + $sessionRecommendedNumber[$queueItem['session_id']] = $row['value']; +} +// Group student by superior and session +$queueBySuperior = []; +foreach ($queueList as $queueItem) { + $queueBySuperior[$queueItem['session_id']][$queueItem['superior_id']][$queueItem['student_id']]['status'] = $queueItem['status']; +} + +foreach ($queueBySuperior as $sessionId => $superiorStudents) { + $data = [ + 'sessionId' => $sessionId, + 'session' => $sessionInfoList[$sessionId], + 'students' => [], + ]; + $dataUrl = [ + 'action' => 'confirm', + 'sessionId' => $sessionId, + 'currentUserId' => 0, + 'newStatus' => ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED, + 'studentUserId' => 0, + 'is_connected' => true, + 'profile_completed' => 0, + ]; + foreach ($superiorStudents as $superiorId => $students) { + $data['superior'] = api_get_user_info($superiorId); + // Check if superior has at least one student + if (count($students) > 0) { + foreach ($students as $studentId => $studentInfo) { + if ($studentInfo['status'] == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START) { + $data['students'][$studentId] = api_get_user_info($studentId); + $dataUrl['studentUserId'] = $studentId; + $dataUrl['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED; + $data['students'][$studentId]['acceptUrl'] = $plugin->getQueueUrl($dataUrl); + $dataUrl['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED; + $data['students'][$studentId]['rejectUrl'] = $plugin->getQueueUrl($dataUrl); + } + } + + if (is_array($data['students']) && count($data['students']) > 0) { + // Check if superior have more than recommended + if ($sessionRecommendedNumber[$sessionId] >= count($students)) { + // Is greater or equal than recommended + $plugin->sendMail($data, ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR); + } else { + // Is less than recommended + $plugin->sendMail($data, ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR_MAX); + } + } + } + } +} + +/** + * Remind admins. + */ +$admins = UserManager::get_all_administrators(); +$isWesternNameOrder = api_is_western_name_order(); +foreach ($admins as &$admin) { + $admin['complete_name'] = $isWesternNameOrder ? + $admin['firstname'].', '.$admin['lastname'] : $admin['lastname'].', '.$admin['firstname'] + ; +} +unset($admin); +$queueByAdmin = []; +foreach ($queueList as $queueItem) { + if ($queueItem['status'] == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED) { + $queueByAdmin[$queueItem['session_id']]['students'][$queueItem['student_id']]['user_id'] = $queueItem['student_id']; + } +} +$data = [ + 'admins' => $admins, +]; +foreach ($queueByAdmin as $sessionId => $studentInfo) { + $data['sessionId'] = $sessionId; + $data['admin_view_url'] = api_get_path(WEB_PLUGIN_PATH). + 'advanced_subscription/src/admin_view.php?s='.$data['sessionId']; + $data['session'] = $sessionInfoList[$sessionId]; + $data['students'] = $studentInfo['students']; + $plugin->sendMail($data, ADVANCED_SUBSCRIPTION_ACTION_REMINDER_ADMIN); +} diff --git a/plugin/advanced_subscription/index.html b/plugin/advanced_subscription/index.html new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/plugin/advanced_subscription/index.html @@ -0,0 +1 @@ + diff --git a/plugin/advanced_subscription/install.php b/plugin/advanced_subscription/install.php new file mode 100644 index 000000000..846f6de68 --- /dev/null +++ b/plugin/advanced_subscription/install.php @@ -0,0 +1,17 @@ +install(); diff --git a/plugin/advanced_subscription/lang/english.php b/plugin/advanced_subscription/lang/english.php new file mode 100644 index 000000000..4ee55705f --- /dev/null +++ b/plugin/advanced_subscription/lang/english.php @@ -0,0 +1,149 @@ +not include induction courses"; +$strings['course_session_credit_year_start_date'] = 'Year start date'; +$strings['course_session_credit_year_start_date_help'] = "a date (dd/mm)"; +$strings['min_profile_percentage'] = "Minimum required of completed percentage profile"; +$strings['min_profile_percentage_help'] = "Percentage number( > 0.00 y < 100.00)"; +$strings['secret_key'] = 'Secret key'; +$strings['terms_and_conditions'] = 'Terms and conditions'; + +/* String for error message about requirements */ +$strings['AdvancedSubscriptionNotConnected'] = "You are not connected to platform. Please login first"; +$strings['AdvancedSubscriptionProfileIncomplete'] = "You must complete at least %d percent of your profile. You have only completed %d percent at this point"; +$strings['AdvancedSubscriptionIncompleteInduction'] = "You have not yet completed induction course. Please complete it first"; +$strings['AdvancedSubscriptionCostXLimitReached'] = "We are sorry, you have already reached yearly limit %s TUV cost for courses "; +$strings['AdvancedSubscriptionTimeXLimitReached'] = "We are sorry, you have already reached yearly limit %s hours for courses"; +$strings['AdvancedSubscriptionCourseXLimitReached'] = "We are sorry, you have already reached yearly limit %s times for courses"; +$strings['AdvancedSubscriptionNotMoreAble'] = "We are sorry, you no longer fulfills the initial conditions to subscribe this course"; +$strings['AdvancedSubscriptionIncompleteParams'] = "The parameters are wrong or incomplete."; +$strings['AdvancedSubscriptionIsNotEnabled'] = "Advanced subscription is not enabled"; + +$strings['AdvancedSubscriptionNoQueue'] = "You are not subscribed for this course."; +$strings['AdvancedSubscriptionNoQueueIsAble'] = "You are not subscribed, but you are qualified for this course."; +$strings['AdvancedSubscriptionQueueStart'] = "Your subscription request is pending for approval by your boss, please wait attentive."; +$strings['AdvancedSubscriptionQueueBossDisapproved'] = "We are sorry, your subscription was rejected by your boss."; +$strings['AdvancedSubscriptionQueueBossApproved'] = "Your subscription request has been accepted by your boss, now is pending for vacancies."; +$strings['AdvancedSubscriptionQueueAdminDisapproved'] = "We are sorry, your subscription was rejected by the administrator."; +$strings['AdvancedSubscriptionQueueAdminApproved'] = "Congratulations!, your subscription request has been accepted by administrator."; +$strings['AdvancedSubscriptionQueueDefaultX'] = "There was an error, queue status %d is not defined by system."; + +// Mail translations +$strings['MailStudentRequest'] = 'Student registration request'; +$strings['MailBossAccept'] = 'Registration request accepted by boss'; +$strings['MailBossReject'] = 'Registration request rejected by boss'; +$strings['MailStudentRequestSelect'] = 'Student registration requests selection'; +$strings['MailAdminAccept'] = 'Registration request accepted by administrator'; +$strings['MailAdminReject'] = 'Registration request rejected by administrator'; +$strings['MailStudentRequestNoBoss'] = 'Student registration request without boss'; +$strings['MailRemindStudent'] = 'Subscription request reminder'; +$strings['MailRemindSuperior'] = 'Subscription request are pending your approval'; +$strings['MailRemindAdmin'] = 'Course subscription are pending your approval'; + +// TPL langs +$strings['SessionXWithoutVacancies'] = "The course \"%s\" has no vacancies."; +$strings['SuccessSubscriptionToSessionX'] = "

¡Congratulations!

Your subscription to \"%s\" course has been completed successfully."; +$strings['SubscriptionToOpenSession'] = "Subscription to open course"; +$strings['GoToSessionX'] = "Go to \"%s\" course"; +$strings['YouAreAlreadySubscribedToSessionX'] = "You are already subscribed to \"%s\" course."; + +// Admin view +$strings['SelectASession'] = 'Select a training session'; +$strings['SessionName'] = 'Session name'; +$strings['Target'] = 'Target audience'; +$strings['Vacancies'] = 'Vacancies'; +$strings['RecommendedNumberOfParticipants'] = 'Recommended number of participants by area'; +$strings['PublicationEndDate'] = 'Publication end date'; +$strings['Mode'] = 'Mode'; +$strings['Postulant'] = 'Postulant'; +$strings['Area'] = 'Area'; +$strings['Institution'] = 'Institution'; +$strings['InscriptionDate'] = 'Inscription date'; +$strings['BossValidation'] = 'Boss validation'; +$strings['Decision'] = 'Decision'; +$strings['AdvancedSubscriptionAdminViewTitle'] = 'Subscription request confirmation result'; + +$strings['AcceptInfinitive'] = 'Accept'; +$strings['RejectInfinitive'] = 'Reject'; +$strings['AreYouSureYouWantToAcceptSubscriptionOfX'] = 'Are you sure you want to accept the subscription of %s?'; +$strings['AreYouSureYouWantToRejectSubscriptionOfX'] = 'Are you sure you want to reject the subscription of %s?'; + +$strings['MailTitle'] = 'Received request for course %s'; +$strings['MailDear'] = 'Dear:'; +$strings['MailThankYou'] = 'Thank you.'; +$strings['MailThankYouCollaboration'] = 'Thank you for your help.'; + +// Admin Accept +$strings['MailTitleAdminAcceptToAdmin'] = 'Notification: subscription validation received'; +$strings['MailContentAdminAcceptToAdmin'] = 'We have received and registered your subscription validation for student %s to course %s'; +$strings['MailTitleAdminAcceptToStudent'] = 'Accepted: Your subscription to course %s has been accepted!'; +$strings['MailContentAdminAcceptToStudent'] = 'We are pleased to inform you that your registration to course %s starting on %s was validated by an administrator. We wish you good luck and hope you will consider participating to another course soon.'; +$strings['MailTitleAdminAcceptToSuperior'] = 'Notification: Subscription validation of %s to course %s'; +$strings['MailContentAdminAcceptToSuperior'] = 'Subscription of student %s to course %s starting on %s was pending but has been validated a few minutes ago. We kindly hope we can count on you to ensure the necessary availability of your collaborator during the course period.'; + +// Admin Reject +$strings['MailTitleAdminRejectToAdmin'] = 'Notification: Rejection received'; +$strings['MailContentAdminRejectToAdmin'] = 'We have received and registered your rejection for the subscription of student %s to course %s'; +$strings['MailTitleAdminRejectToStudent'] = 'Your subscription to course %s was rejected'; +$strings['MailContentAdminRejectToStudent'] = 'We regret to inform you that your subscription to course %s starting on %s was rejected because of a lack of vacancies. We hope you will consider participating to another course soon.'; +$strings['MailTitleAdminRejectToSuperior'] = 'Notification: Subscription refusal for student %s to course %s'; +$strings['MailContentAdminRejectToSuperior'] = 'The subscription of %s to course %s that you previously validated was rejected because of a lack of vacancies. Our sincere apologies.'; + +// Superior Accept +$strings['MailTitleSuperiorAcceptToAdmin'] = 'Approval for subscription of %s to course %s '; +$strings['MailContentSuperiorAcceptToAdmin'] = 'The subscription of student %s to course %s has been accepted by his superior. You can manage subscriptions here'; +$strings['MailTitleSuperiorAcceptToSuperior'] = 'Confirmation: Approval received for %s'; +$strings['MailContentSuperiorAcceptToSuperior'] = 'We have received and registered you validation of subscription to course %s of your collaborator %s'; +$strings['MailContentSuperiorAcceptToSuperiorSecond'] = 'The subscription is now pending for a vacancies confirmation. We will keep you informed about changes of status for this subscription'; +$strings['MailTitleSuperiorAcceptToStudent'] = 'Accepted: Your subscription to course %s has been approved by your superior'; +$strings['MailContentSuperiorAcceptToStudent'] = 'We are pleased to inform you that your subscription to course %s has been accepted by your superior. Your inscription is now pending for a vacancies confirmation. We will notify you as soon as it is confirmed.'; + +// Superior Reject +$strings['MailTitleSuperiorRejectToStudent'] = 'Notification: Your subscription to course %s has been refused'; +$strings['MailContentSuperiorRejectToStudent'] = 'We regret to inform your subscription to course %s was NOT accepted. We hope this will not reduce your motivation and encourage you to register to another course or, on another occasion, this same course soon.'; +$strings['MailTitleSuperiorRejectToSuperior'] = 'Confirmation: Rejection of subscription received for %s'; +$strings['MailContentSuperiorRejectToSuperior'] = 'We have received and registered your rejection of subscription to course %s for your collaborator %s'; + +// Student Request +$strings['MailTitleStudentRequestToStudent'] = 'Notification: Subscription approval received'; +$strings['MailContentStudentRequestToStudent'] = 'We have received and registered your subscription request to course %s starting on %s'; +$strings['MailContentStudentRequestToStudentSecond'] = 'Your subscription is pending approval, first from your superior, then for the availability of vacancies. An email has been sent to your superior for review and approval. We will inform you when this situation changes.'; +$strings['MailTitleStudentRequestToSuperior'] = 'Course subscription request from your collaborator'; +$strings['MailContentStudentRequestToSuperior'] = 'We have received an subscription request of %s to course %s, starting on %s. Course details: %s.'; +$strings['MailContentStudentRequestToSuperiorSecond'] = 'Your are welcome to accept or reject this subscription, clicking the corresponding button.'; + +// Student Request No Boss +$strings['MailTitleStudentRequestNoSuperiorToStudent'] = 'Your subscription request for %s has been received'; +$strings['MailContentStudentRequestNoSuperiorToStudent'] = 'We have received and registered your subscription to course %s starting on %s.'; +$strings['MailContentStudentRequestNoSuperiorToStudentSecond'] = 'Your subscription is pending availability of vacancies. You will get the results of your request approval (or rejection) soon.'; +$strings['MailTitleStudentRequestNoSuperiorToAdmin'] = 'Subscription request of %s to course %s'; +$strings['MailContentStudentRequestNoSuperiorToAdmin'] = 'The subscription of %s to course %s has been approved by default (no direct superior defined). You can manage subscriptions here'; + +// Reminders +$strings['MailTitleReminderAdmin'] = 'Subscriptions to %s are pending confirmation'; +$strings['MailContentReminderAdmin'] = 'The subscription requests for course %s are pending validation to be accepted. Please, go to Administration page to validate them.'; +$strings['MailTitleReminderStudent'] = 'Information: Your subscription request is pending approval for course %s'; +$strings['MailContentReminderStudent'] = 'This email is just to confirm we have received and registered your subscription request to course %s, starting on %s.'; +$strings['MailContentReminderStudentSecond'] = 'Your subscription has not been approved by your superior yet, so we sent him a e-mail reminder.'; +$strings['MailTitleReminderSuperior'] = 'Course subscription request for your collaborators'; +$strings['MailContentReminderSuperior'] = 'We kindly remind you that we have received the subscription requests below to course %s from your collaborators. This course is starting on %s. Course details: %s.'; +$strings['MailContentReminderSuperiorSecond'] = 'We invite you to accept or reject this subscription request by clicking the corresponding button for each collaborator.'; +$strings['MailTitleReminderMaxSuperior'] = 'Reminder: Course subscription request for your collaborators'; +$strings['MailContentReminderMaxSuperior'] = 'We kindly remind you that we have received the subscription requests below to course %s from your collaborators. This course is starting on %s. Course details: %s.'; +$strings['MailContentReminderMaxSuperiorSecond'] = 'This course have limited vacancies and has received a high subscription request rate, So we recommend all areas to accept at most %s candidates. We invite you to accept or reject the inscription request by clicking the corresponding button for each collaborator.'; + +$strings['YouMustAcceptTermsAndConditions'] = 'To subscribe to course %s, you must accept these terms and conditions.'; diff --git a/plugin/advanced_subscription/lang/french.php b/plugin/advanced_subscription/lang/french.php new file mode 100644 index 000000000..a6d1af5c0 --- /dev/null +++ b/plugin/advanced_subscription/lang/french.php @@ -0,0 +1,148 @@ +ne sont pas le cours d'induction"; +$strings['course_session_credit_year_start_date'] = 'Date de début'; +$strings['course_session_credit_year_start_date_help'] = "Date de début de l'année (jour/mois)"; +$strings['min_profile_percentage'] = 'Pourcentage du profil complété mínimum requis'; +$strings['min_profile_percentage_help'] = 'Numéro pourcentage ( > 0.00 et < 100.00)'; +$strings['secret_key'] = 'Clef secrète'; +$strings['terms_and_conditions'] = 'Conditions d\'utilisation'; + +/* String for error message about requirements */ +$strings['AdvancedSubscriptionNotConnected'] = "Vous n'êtes pas connecté à la plateforme. Merci d'introduire votre nom d'utilisateur / mot de passe afin de vous inscrire"; +$strings['AdvancedSubscriptionProfileIncomplete'] = "Vous devez d'abord compléter votre profil à %d pourcents ou plus. Pour l'instant vous n'avez complété que %d pourcents"; +$strings['AdvancedSubscriptionIncompleteInduction'] = "Vous n'avez pas encore passé le cours d'induction. Merci de commencer par cette étape."; +$strings['AdvancedSubscriptionCostXLimitReached'] = "Désolé, vous avez déjà atteint la limite de %s unités de taxe pour les cours que vous avez suivi cette année"; +$strings['AdvancedSubscriptionTimeXLimitReached'] = "Désolé, vous avez déjà atteint la limite annuelle du nombre de %s heures pour les cours que vous avez suivi cette année"; +$strings['AdvancedSubscriptionCourseXLimitReached'] = "Désolé, vous avez déjà atteint la limite annuelle du nombre de cours (%s) à suivre cette année"; +$strings['AdvancedSubscriptionNotMoreAble'] = "Désolé, vous ne répondez plus aux conditions d'utilisation minimum pour l'inscription à un cours"; +$strings['AdvancedSubscriptionIncompleteParams'] = "Les paramètres envoyés ne sont pas complets ou sont incorrects."; +$strings['AdvancedSubscriptionIsNotEnabled'] = "L'inscription avancée n'est pas activée"; +$strings['AdvancedSubscriptionNoQueue'] = "Vous n'êtes pas inscrit dans ce cours"; +$strings['AdvancedSubscriptionNoQueueIsAble'] = "Vous n'êtes pas inscrit mais vous qualifiez pour ce cours"; +$strings['AdvancedSubscriptionQueueStart'] = "Votre demande d'inscription est en attente de l'approbation de votre supérieur(e). Merci de patienter."; +$strings['AdvancedSubscriptionQueueBossDisapproved'] = "Désolé, votre inscription a été déclinée par votre supérieur(e)."; +$strings['AdvancedSubscriptionQueueBossApproved'] = "Votre demande d'inscription a été acceptée par votre supérieur(e), mais est en attente de places libres."; +$strings['AdvancedSubscriptionQueueAdminDisapproved'] = "Désolé, votre inscription a été déclinée par l'administrateur."; +$strings['AdvancedSubscriptionQueueAdminApproved'] = "Félicitations! Votre inscription a été acceptée par l'administrateur."; +$strings['AdvancedSubscriptionQueueDefaultX'] = "Une erreur est survenue: l'état de la file d'attente %s n'est pas défini dans le système."; + +// Mail translations +$strings['MailStudentRequest'] = 'Demange d\'inscription d\'un(e) apprenant(e)'; +$strings['MailBossAccept'] = 'Demande d\'inscription acceptée par votre supérieur(e)'; +$strings['MailBossReject'] = 'Demande d\'inscription déclinée par votre supérieur(e)'; +$strings['MailStudentRequestSelect'] = 'Sélection des demandes d\'inscriptions d\'apprenants'; +$strings['MailAdminAccept'] = 'Demande d\'inscription acceptée par l\'administrateur'; +$strings['MailAdminReject'] = 'Demande d\'inscription déclinée par l\'administrateur'; +$strings['MailStudentRequestNoBoss'] = 'Demande d\'inscription d\'apprenant sans supérieur(e)'; +$strings['MailRemindStudent'] = 'Rappel de demande d\'inscription'; +$strings['MailRemindSuperior'] = 'Demandes d\'inscription en attente de votre approbation'; +$strings['MailRemindAdmin'] = 'Inscriptions en attente de votre approbation'; + +// TPL translations +$strings['SessionXWithoutVacancies'] = "Le cours \"%s\" ne dispose plus de places libres."; +$strings['SuccessSubscriptionToSessionX'] = "

Félicitations!

Votre inscription au cours \"%s\" est en ordre."; +$strings['SubscriptionToOpenSession'] = "Inscription à cours ouvert"; +$strings['GoToSessionX'] = "Aller dans le cours \"%s\""; +$strings['YouAreAlreadySubscribedToSessionX'] = "Vous êtes déjà inscrit(e) au cours \"%s\"."; + +// Admin view +$strings['SelectASession'] = 'Sélectionnez une session de formation'; +$strings['SessionName'] = 'Nom de la session'; +$strings['Target'] = 'Public cible'; +$strings['Vacancies'] = 'Places libres'; +$strings['RecommendedNumberOfParticipants'] = 'Nombre recommandé de participants par département'; +$strings['PublicationEndDate'] = 'Date de fin de publication'; +$strings['Mode'] = 'Modalité'; +$strings['Postulant'] = 'Candidats'; +$strings['Area'] = 'Département'; +$strings['Institution'] = 'Institution'; +$strings['InscriptionDate'] = 'Date d\'inscription'; +$strings['BossValidation'] = 'Validation du supérieur'; +$strings['Decision'] = 'Décision'; +$strings['AdvancedSubscriptionAdminViewTitle'] = 'Résultat de confirmation de demande d\'inscription'; + +$strings['AcceptInfinitive'] = 'Accepter'; +$strings['RejectInfinitive'] = 'Refuser'; +$strings['AreYouSureYouWantToAcceptSubscriptionOfX'] = 'Êtes-vous certain de vouloir accepter l\'inscription de %s?'; +$strings['AreYouSureYouWantToRejectSubscriptionOfX'] = 'Êtes-vous certain de vouloir refuser l\'inscription de %s?'; + +$strings['MailTitle'] = 'Demande reçue pour le cours %s'; +$strings['MailDear'] = 'Cher/Chère'; +$strings['MailThankYou'] = 'Merci.'; +$strings['MailThankYouCollaboration'] = 'Merci de votre collaboration.'; + +// Admin Accept +$strings['MailTitleAdminAcceptToAdmin'] = 'Information: Validation d\'inscription reçue'; +$strings['MailContentAdminAcceptToAdmin'] = 'Nous avons bien reçu et enregistré votre validation de l\'inscription de %s au cours %s'; +$strings['MailTitleAdminAcceptToStudent'] = 'Approuvé(e): Votre inscription au cours %s a été confirmée!'; +$strings['MailContentAdminAcceptToStudent'] = 'C\'est avec plaisir que nous vous informons que votre inscription au cours %s démarrant le %s a été validée par les administrateurs. Nous espérons que votre motivation s\'est maintenue à 100% et que vous participerez à d\'autres cours ou répétiez ce cours à l\'avenir.'; +$strings['MailTitleAdminAcceptToSuperior'] = 'Information: Validation de l\'inscription de %s au cours %s'; +$strings['MailContentAdminAcceptToSuperior'] = 'L\'inscription de %s au cours %s qui démarre le %s, qui était en attente de validation par les organisateurs du cours, vient d\'être validée. Nous espérons que vous nous donnerez un coup de main pour assurer la disponibilité complète de votre collaborateur pour toute la durée du cours'; + +// Admin Reject +$strings['MailTitleAdminRejectToAdmin'] = 'Information: refus d\'inscription reçu'; +$strings['MailContentAdminRejectToAdmin'] = 'Nous avons bien reçu et enregistré votre refus pour l\'inscription de %s au cours %s'; +$strings['MailTitleAdminRejectToStudent'] = 'Votre demande d\'inscription au cours %s a été refusée'; +$strings['MailContentAdminRejectToStudent'] = 'Nous déplorons le besoin de vous informer que vote demande d\'inscription au cours %s démarrant le %s a été refusée pour manque de place. Nous espérons que vous maintiendrez votre motivation et que vous pourrez participer au même ou à un autre cours lors d\'une prochaine occasion.'; +$strings['MailTitleAdminRejectToSuperior'] = 'Information: Refus d\'inscription de %s au cours %s'; +$strings['MailContentAdminRejectToSuperior'] = 'L\'inscription de %s au cours %s, qui avait été approuvée antérieurement, a été refusée par manque de place. Nous vous présentons nos excuses sincères.'; + +// Superior Accept +$strings['MailTitleSuperiorAcceptToAdmin'] = 'Aprobación de %s al curso %s '; +$strings['MailContentSuperiorAcceptToAdmin'] = 'La inscripción del alumno %s al curso %s ha sido aprobada por su superior. Puede gestionar las inscripciones al curso aquí'; +$strings['MailTitleSuperiorAcceptToSuperior'] = 'Confirmación: Aprobación recibida para %s'; +$strings['MailContentSuperiorAcceptToSuperior'] = 'Hemos recibido y registrado su decisión de aprobar el curso %s para su colaborador %s'; +$strings['MailContentSuperiorAcceptToSuperiorSecond'] = 'Ahora la inscripción al curso está pendiente de la disponibilidad de cupos. Le mantendremos informado sobre el resultado de esta etapa'; +$strings['MailTitleSuperiorAcceptToStudent'] = 'Aprobado: Su inscripción al curso %s ha sido aprobada por su superior '; +$strings['MailContentSuperiorAcceptToStudent'] = 'Nos complace informarle que su inscripción al curso %s ha sido aprobada por su superior. Su inscripción ahora solo se encuentra pendiente de disponibilidad de cupos. Le avisaremos tan pronto como se confirme este último paso.'; + +// Superior Reject +$strings['MailTitleSuperiorRejectToStudent'] = 'Información: Su inscripción al curso %s ha sido rechazada '; +$strings['MailContentSuperiorRejectToStudent'] = 'Lamentamos informarle que, en esta oportunidad, su inscripción al curso %s NO ha sido aprobada. Esperamos mantenga todo su ánimo y participe en otro curso o, en otra oportunidad, a este mismo curso.'; +$strings['MailTitleSuperiorRejectToSuperior'] = 'Confirmación: Desaprobación recibida para %s'; +$strings['MailContentSuperiorRejectToSuperior'] = 'Hemos recibido y registrado su decisión de desaprobar el curso %s para su colaborador %s'; + +// Student Request +$strings['MailTitleStudentRequestToStudent'] = 'Información: Validación de inscripción recibida'; +$strings['MailContentStudentRequestToStudent'] = 'Hemos recibido y registrado su solicitud de inscripción al curso %s para iniciarse el %s.'; +$strings['MailContentStudentRequestToStudentSecond'] = 'Su inscripción es pendiente primero de la aprobación de su superior, y luego de la disponibilidad de cupos. Un correo ha sido enviado a su superior para revisión y aprobación de su solicitud.'; +$strings['MailTitleStudentRequestToSuperior'] = 'Solicitud de consideración de curso para un colaborador'; +$strings['MailContentStudentRequestToSuperior'] = 'Hemos recibido una solicitud de inscripción de %s al curso %s, por iniciarse el %s. Detalles del curso: %s.'; +$strings['MailContentStudentRequestToSuperiorSecond'] = 'Le invitamos a aprobar o desaprobar esta inscripción, dando clic en el botón correspondiente a continuación.'; + +// Student Request No Boss +$strings['MailTitleStudentRequestNoSuperiorToStudent'] = 'Solicitud recibida para el curso %s'; +$strings['MailContentStudentRequestNoSuperiorToStudent'] = 'Hemos recibido y registrado su solicitud de inscripción al curso %s para iniciarse el %s.'; +$strings['MailContentStudentRequestNoSuperiorToStudentSecond'] = 'Su inscripción es pendiente de la disponibilidad de cupos. Pronto recibirá los resultados de su aprobación de su solicitud.'; +$strings['MailTitleStudentRequestNoSuperiorToAdmin'] = 'Solicitud de inscripción de %s para el curso %s'; +$strings['MailContentStudentRequestNoSuperiorToAdmin'] = 'La inscripción del alumno %s al curso %s ha sido aprobada por defecto, a falta de superior. Puede gestionar las inscripciones al curso aquí'; + +// Reminders +$strings['MailTitleReminderAdmin'] = 'Inscripciones a %s pendiente de confirmación'; +$strings['MailContentReminderAdmin'] = 'Las inscripciones siguientes al curso %s están pendientes de validación para ser efectivas. Por favor, dirigese a la página de administración para validarlos.'; +$strings['MailTitleReminderStudent'] = 'Información: Solicitud pendiente de aprobación para el curso %s'; +$strings['MailContentReminderStudent'] = 'Este correo es para confirmar que hemos recibido y registrado su solicitud de inscripción al curso %s, por iniciarse el %s.'; +$strings['MailContentReminderStudentSecond'] = 'Su inscripción todavía no ha sido aprobada por su superior, por lo que hemos vuelto a enviarle un correo electrónico de recordatorio.'; +$strings['MailTitleReminderSuperior'] = 'Solicitud de consideración de curso para un colaborador'; +$strings['MailContentReminderSuperior'] = 'Le recordamos que hemos recibido las siguientes solicitudes de suscripción para el curso %s de parte de sus colaboradores. El curso se iniciará el %s. Detalles del curso: %s.'; +$strings['MailContentReminderSuperiorSecond'] = 'Le invitamos a aprobar o desaprobar las suscripciones, dando clic en el botón correspondiente a continuación para cada colaborador.'; +$strings['MailTitleReminderMaxSuperior'] = 'Recordatorio: Solicitud de consideración de curso para colaborador(es)'; +$strings['MailContentReminderMaxSuperior'] = 'Le recordamos que hemos recibido las siguientes solicitudes de suscripción al curso %s de parte de sus colaboradores. El curso se iniciará el %s. Detalles del curso: %s.'; +$strings['MailContentReminderMaxSuperiorSecond'] = 'Este curso tiene una cantidad de cupos limitados y ha recibido una alta tasa de solicitudes de inscripción, por lo que recomendamos que cada área apruebe un máximo de %s candidatos. Le invitamos a aprobar o desaprobar las suscripciones, dando clic en el botón correspondiente a continuación para cada colaborador.'; + +$strings['YouMustAcceptTermsAndConditions'] = 'Para inscribirse al curso %s, debe aceptar estos términos y condiciones.'; diff --git a/plugin/advanced_subscription/lang/spanish.php b/plugin/advanced_subscription/lang/spanish.php new file mode 100644 index 000000000..9c5633c57 --- /dev/null +++ b/plugin/advanced_subscription/lang/spanish.php @@ -0,0 +1,149 @@ +no sean el curso de inducción"; +$strings['course_session_credit_year_start_date'] = 'Fecha de inicio'; +$strings['course_session_credit_year_start_date_help'] = "Fecha de inicio del año (día/mes)"; +$strings['min_profile_percentage'] = 'Porcentage de perfil completado mínimo requerido'; +$strings['min_profile_percentage_help'] = 'Número porcentage ( > 0.00 y < 100.00)'; +$strings['secret_key'] = 'LLave secreta'; +$strings['terms_and_conditions'] = 'Términos y condiciones'; + +/* String for error message about requirements */ +$strings['AdvancedSubscriptionNotConnected'] = "Usted no está conectado en la plataforma. Por favor ingrese su usuario / constraseña para poder inscribirse"; +$strings['AdvancedSubscriptionProfileIncomplete'] = "Debe llenar el %d porciento de tu perfil como mínimo. Por ahora has llenado el %d porciento"; +$strings['AdvancedSubscriptionIncompleteInduction'] = "Usted aún no ha completado el curso de inducción. Por favor complete el curso inducción"; +$strings['AdvancedSubscriptionCostXLimitReached'] = "Lo sentimos, usted ya ha alcanzado el límite anual de %s UIT para los cursos que ha seguido este año"; +$strings['AdvancedSubscriptionTimeXLimitReached'] = "Lo sentimos, usted ya ha alcanzado el límite anual de %s horas para los cursos que ha seguido este año"; +$strings['AdvancedSubscriptionCourseXLimitReached'] = "Lo sentimos, usted ya ha alcanzado el límite anual de %s cursos que ha seguido este año"; +$strings['AdvancedSubscriptionNotMoreAble'] = "Lo sentimos, usted ya no cumple con las condiciones iniciales para poder inscribirse al curso"; +$strings['AdvancedSubscriptionIncompleteParams'] = "Los parámetros enviados no están completos o no son los correctos."; +$strings['AdvancedSubscriptionIsNotEnabled'] = "La inscripción avanzada no está activada"; + +$strings['AdvancedSubscriptionNoQueue'] = "Usted no está inscrito para este curso."; +$strings['AdvancedSubscriptionNoQueueIsAble'] = "Usted no está inscrito, pero está calificado para este curso."; +$strings['AdvancedSubscriptionQueueStart'] = "Su solicitud de inscripción está pendiente de la aprobación de su jefe, por favor espere atentamente."; +$strings['AdvancedSubscriptionQueueBossDisapproved'] = "Lo sentimos, tu inscripción fue rechazada por tu jefe."; +$strings['AdvancedSubscriptionQueueBossApproved'] = "Tu solicitud de inscripción ha sido aceptado por tu jefe, ahora está en espera de vacantes."; +$strings['AdvancedSubscriptionQueueAdminDisapproved'] = "Lo sentimos, Tu inscripción ha sido rechazada por el administrador."; +$strings['AdvancedSubscriptionQueueAdminApproved'] = "¡Felicitaciones! Tu inscripción ha sido aceptada por el administrador."; +$strings['AdvancedSubscriptionQueueDefaultX'] = "Hubo un error el estado en cola %d no está definido en el sistema."; + +// Mail translations +$strings['MailStudentRequest'] = 'Solicitud de registro de estudiante'; +$strings['MailBossAccept'] = 'Solicitud de registro aceptada por superior'; +$strings['MailBossReject'] = 'Solicitud de registro rechazada por superior'; +$strings['MailStudentRequestSelect'] = 'Selección de solicitudes de registro de estudiante'; +$strings['MailAdminAccept'] = 'Solicitud de registro aceptada por administrador'; +$strings['MailAdminReject'] = 'Solicitud de registro rechazada por administrador'; +$strings['MailStudentRequestNoBoss'] = 'Solicitud de registro de estudiante sin superior'; +$strings['MailRemindStudent'] = 'Recordatorio de la solicitud de inscripción'; +$strings['MailRemindSuperior'] = 'Solicitudes de inscripción estan pendientes de tu aprobación'; +$strings['MailRemindAdmin'] = 'Inscripciones de cursos estan pendientes de tu aprobación'; + +// TPL translations +$strings['SessionXWithoutVacancies'] = "El curso \"%s\" no tiene cupos disponibles."; +$strings['SuccessSubscriptionToSessionX'] = "

¡Felicitaciones!

Tu inscripción al curso \"%s\" se realizó correctamente."; +$strings['SubscriptionToOpenSession'] = "Inscripcion a curso abierto"; +$strings['GoToSessionX'] = "Ir al curso \"%s\""; +$strings['YouAreAlreadySubscribedToSessionX'] = "Usted ya está inscrito al curso \"%s\"."; + +// Admin view +$strings['SelectASession'] = 'Elija una sesión de formación'; +$strings['SessionName'] = 'Nombre de la sesión'; +$strings['Target'] = 'Publico objetivo'; +$strings['Vacancies'] = 'Vacantes'; +$strings['RecommendedNumberOfParticipants'] = 'Número recomendado de participantes por área'; +$strings['PublicationEndDate'] = 'Fecha fin de publicación'; +$strings['Mode'] = 'Modalidad'; +$strings['Postulant'] = 'Postulante'; +$strings['Area'] = 'Área'; +$strings['Institution'] = 'Institución'; +$strings['InscriptionDate'] = 'Fecha de inscripción'; +$strings['BossValidation'] = 'Validación del superior'; +$strings['Decision'] = 'Decisión'; +$strings['AdvancedSubscriptionAdminViewTitle'] = 'Resultado de confirmación de solicitud de inscripción'; + +$strings['AcceptInfinitive'] = 'Aceptar'; +$strings['RejectInfinitive'] = 'Rechazar'; +$strings['AreYouSureYouWantToAcceptSubscriptionOfX'] = '¿Está seguro que quiere aceptar la inscripción de %s?'; +$strings['AreYouSureYouWantToRejectSubscriptionOfX'] = '¿Está seguro que quiere rechazar la inscripción de %s?'; + +$strings['MailTitle'] = 'Solicitud recibida para el curso %s'; +$strings['MailDear'] = 'Estimado(a):'; +$strings['MailThankYou'] = 'Gracias.'; +$strings['MailThankYouCollaboration'] = 'Gracias por su colaboración.'; + +// Admin Accept +$strings['MailTitleAdminAcceptToAdmin'] = 'Información: Validación de inscripción recibida'; +$strings['MailContentAdminAcceptToAdmin'] = 'Hemos recibido y registrado su validación de la inscripción de %s al curso %s'; +$strings['MailTitleAdminAcceptToStudent'] = 'Aprobada: ¡Su inscripción al curso %s fue confirmada!'; +$strings['MailContentAdminAcceptToStudent'] = 'Nos complace informarle que su inscripción al curso %s iniciando el %s fue validada por los administradores. Esperamos mantenga todo su ánimo y participe en otro curso o, en otra oportunidad, a este mismo curso.'; +$strings['MailTitleAdminAcceptToSuperior'] = 'Información: Validación de inscripción de %s al curso %s'; +$strings['MailContentAdminAcceptToSuperior'] = 'La inscripción de %s al curso %s iniciando el %s, que estaba pendiente de validación por los organizadores del curso, fue validada hacen unos minutos. Esperamos nos ayude en asegurar la completa disponibilidad de su colaborador(a) para la duración completa del curso.'; + +// Admin Reject +$strings['MailTitleAdminRejectToAdmin'] = 'Información: rechazo de inscripción recibido'; +$strings['MailContentAdminRejectToAdmin'] = 'Hemos recibido y registrado su rechazo de la inscripción de %s al curso %s'; +$strings['MailTitleAdminRejectToStudent'] = 'Rechazamos su inscripción al curso %s'; +$strings['MailContentAdminRejectToStudent'] = 'Lamentamos informarle que su inscripción al curso %s iniciando el %s fue rechazada por falta de cupos. Esperamos mantenga todo su ánimo y participe en otro curso o, en otra oportunidad, a este mismo curso.'; +$strings['MailTitleAdminRejectToSuperior'] = 'Información: Rechazo de inscripción de %s al curso %s'; +$strings['MailContentAdminRejectToSuperior'] = 'La inscripción de %s al curso %s, que había aprobado anteriormente, fue rechazada por falta de cupos. Les presentamos nuestras disculpas sinceras.'; + +// Superior Accept +$strings['MailTitleSuperiorAcceptToAdmin'] = 'Aprobación de %s al curso %s '; +$strings['MailContentSuperiorAcceptToAdmin'] = 'La inscripción del alumno %s al curso %s ha sido aprobada por su superior. Puede gestionar las inscripciones al curso aquí'; +$strings['MailTitleSuperiorAcceptToSuperior'] = 'Confirmación: Aprobación recibida para %s'; +$strings['MailContentSuperiorAcceptToSuperior'] = 'Hemos recibido y registrado su decisión de aprobar el curso %s para su colaborador %s'; +$strings['MailContentSuperiorAcceptToSuperiorSecond'] = 'Ahora la inscripción al curso está pendiente de la disponibilidad de cupos. Le mantendremos informado sobre el resultado de esta etapa'; +$strings['MailTitleSuperiorAcceptToStudent'] = 'Aprobado: Su inscripción al curso %s ha sido aprobada por su superior '; +$strings['MailContentSuperiorAcceptToStudent'] = 'Nos complace informarle que su inscripción al curso %s ha sido aprobada por su superior. Su inscripción ahora solo se encuentra pendiente de disponibilidad de cupos. Le avisaremos tan pronto como se confirme este último paso.'; + +// Superior Reject +$strings['MailTitleSuperiorRejectToStudent'] = 'Información: Su inscripción al curso %s ha sido rechazada '; +$strings['MailContentSuperiorRejectToStudent'] = 'Lamentamos informarle que, en esta oportunidad, su inscripción al curso %s NO ha sido aprobada. Esperamos mantenga todo su ánimo y participe en otro curso o, en otra oportunidad, a este mismo curso.'; +$strings['MailTitleSuperiorRejectToSuperior'] = 'Confirmación: Desaprobación recibida para %s'; +$strings['MailContentSuperiorRejectToSuperior'] = 'Hemos recibido y registrado su decisión de desaprobar el curso %s para su colaborador %s'; + +// Student Request +$strings['MailTitleStudentRequestToStudent'] = 'Información: Validación de inscripción recibida'; +$strings['MailContentStudentRequestToStudent'] = 'Hemos recibido y registrado su solicitud de inscripción al curso %s para iniciarse el %s.'; +$strings['MailContentStudentRequestToStudentSecond'] = 'Su inscripción es pendiente primero de la aprobación de su superior, y luego de la disponibilidad de cupos. Un correo ha sido enviado a su superior para revisión y aprobación de su solicitud.'; +$strings['MailTitleStudentRequestToSuperior'] = 'Solicitud de consideración de curso para un colaborador'; +$strings['MailContentStudentRequestToSuperior'] = 'Hemos recibido una solicitud de inscripción de %s al curso %s, por iniciarse el %s. Detalles del curso: %s.'; +$strings['MailContentStudentRequestToSuperiorSecond'] = 'Le invitamos a aprobar o desaprobar esta inscripción, dando clic en el botón correspondiente a continuación.'; + +// Student Request No Boss +$strings['MailTitleStudentRequestNoSuperiorToStudent'] = 'Solicitud recibida para el curso %s'; +$strings['MailContentStudentRequestNoSuperiorToStudent'] = 'Hemos recibido y registrado su solicitud de inscripción al curso %s para iniciarse el %s.'; +$strings['MailContentStudentRequestNoSuperiorToStudentSecond'] = 'Su inscripción es pendiente de la disponibilidad de cupos. Pronto recibirá los resultados de su aprobación de su solicitud.'; +$strings['MailTitleStudentRequestNoSuperiorToAdmin'] = 'Solicitud de inscripción de %s para el curso %s'; +$strings['MailContentStudentRequestNoSuperiorToAdmin'] = 'La inscripción del alumno %s al curso %s ha sido aprobada por defecto, a falta de superior. Puede gestionar las inscripciones al curso aquí'; + +// Reminders +$strings['MailTitleReminderAdmin'] = 'Inscripciones a %s pendiente de confirmación'; +$strings['MailContentReminderAdmin'] = 'Las inscripciones siguientes al curso %s están pendientes de validación para ser efectivas. Por favor, dirigese a la página de administración para validarlos.'; +$strings['MailTitleReminderStudent'] = 'Información: Solicitud pendiente de aprobación para el curso %s'; +$strings['MailContentReminderStudent'] = 'Este correo es para confirmar que hemos recibido y registrado su solicitud de inscripción al curso %s, por iniciarse el %s.'; +$strings['MailContentReminderStudentSecond'] = 'Su inscripción todavía no ha sido aprobada por su superior, por lo que hemos vuelto a enviarle un correo electrónico de recordatorio.'; +$strings['MailTitleReminderSuperior'] = 'Solicitud de consideración de curso para un colaborador'; +$strings['MailContentReminderSuperior'] = 'Le recordamos que hemos recibido las siguientes solicitudes de suscripción para el curso %s de parte de sus colaboradores. El curso se iniciará el %s. Detalles del curso: %s.'; +$strings['MailContentReminderSuperiorSecond'] = 'Le invitamos a aprobar o desaprobar las suscripciones, dando clic en el botón correspondiente a continuación para cada colaborador.'; +$strings['MailTitleReminderMaxSuperior'] = 'Recordatorio: Solicitud de consideración de curso para colaborador(es)'; +$strings['MailContentReminderMaxSuperior'] = 'Le recordamos que hemos recibido las siguientes solicitudes de suscripción al curso %s de parte de sus colaboradores. El curso se iniciará el %s. Detalles del curso: %s.'; +$strings['MailContentReminderMaxSuperiorSecond'] = 'Este curso tiene una cantidad de cupos limitados y ha recibido una alta tasa de solicitudes de inscripción, por lo que recomendamos que cada área apruebe un máximo de %s candidatos. Le invitamos a aprobar o desaprobar las suscripciones, dando clic en el botón correspondiente a continuación para cada colaborador.'; + +$strings['YouMustAcceptTermsAndConditions'] = 'Para inscribirse al curso %s, debe aceptar estos términos y condiciones.'; diff --git a/plugin/advanced_subscription/license.txt b/plugin/advanced_subscription/license.txt new file mode 100644 index 000000000..a7619191f --- /dev/null +++ b/plugin/advanced_subscription/license.txt @@ -0,0 +1 @@ +This plugin, as the rest of Chamilo, is released under the GNU/GPLv3 license. diff --git a/plugin/advanced_subscription/plugin.php b/plugin/advanced_subscription/plugin.php new file mode 100644 index 000000000..89cc82a6a --- /dev/null +++ b/plugin/advanced_subscription/plugin.php @@ -0,0 +1,13 @@ +Plugins). + * + * @package chamilo.plugin.advanced_subscription + */ +/** + * Plugin details (must be present). + */ +require_once __DIR__.'/config.php'; +$plugin_info = AdvancedSubscriptionPlugin::create()->get_info(); diff --git a/plugin/advanced_subscription/src/AdvancedSubscriptionPlugin.php b/plugin/advanced_subscription/src/AdvancedSubscriptionPlugin.php new file mode 100644 index 000000000..35f26d4e1 --- /dev/null +++ b/plugin/advanced_subscription/src/AdvancedSubscriptionPlugin.php @@ -0,0 +1,1534 @@ + 'text', + 'yearly_hours_limit' => 'text', + 'yearly_cost_unit_converter' => 'text', + 'courses_count_limit' => 'text', + 'course_session_credit_year_start_date' => 'text', + 'ws_url' => 'text', + 'min_profile_percentage' => 'text', + 'check_induction' => 'boolean', + 'secret_key' => 'text', + 'terms_and_conditions' => 'wysiwyg', + ]; + + parent::__construct('1.0', 'Imanol Losada, Daniel Barreto', $parameters); + $this->errorMessages = []; + } + + /** + * Instance the plugin. + * + * @staticvar null $result + * + * @return AdvancedSubscriptionPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Install the plugin. + */ + public function install() + { + $this->installDatabase(); + $this->addAreaField(); + $this->installHook(); + } + + /** + * Uninstall the plugin. + */ + public function uninstall() + { + $setting = api_get_setting('advanced_subscription'); + if (!empty($setting)) { + $this->uninstallHook(); + // Note: Keeping area field data is intended so it will not be removed + $this->uninstallDatabase(); + } + } + + /** + * Get the error messages list. + * + * @return array The message list + */ + public function getErrorMessages() + { + return $this->errorMessages; + } + + /** + * Check if is allowed subscribe to open session. + * + * @param array $params WS params + * + * @return bool + */ + public function isAllowedSubscribeToOpenSession($params) + { + $self = self::create(); + $wsUrl = $self->get('ws_url'); + $profileCompleted = 0; + if (!empty($wsUrl)) { + $client = new SoapClient( + null, + ['location' => $wsUrl, 'uri' => $wsUrl] + ); + $userInfo = api_get_user_info( + $params['user_id'], + false, + false, + true + ); + + try { + $profileCompleted = $client->__soapCall( + 'getProfileCompletionPercentage', + $userInfo['extra']['drupal_user_id'] + ); + } catch (\Exception $e) { + $profileCompleted = 0; + } + } elseif (isset($params['profile_completed'])) { + $profileCompleted = (float) $params['profile_completed']; + } + $profileCompletedMin = (float) $self->get('min_profile_percentage'); + + if ($profileCompleted < $profileCompletedMin) { + $this->errorMessages[] = sprintf( + $this->get_lang('AdvancedSubscriptionProfileIncomplete'), + $profileCompletedMin, + $profileCompleted + ); + } + + $vacancy = $self->getVacancy($params['session_id']); + $sessionInfo = api_get_session_info($params['session_id']); + + if ($sessionInfo['nbr_users'] >= $vacancy) { + $this->errorMessages[] = sprintf( + $this->get_lang('SessionXWithoutVacancies'), + $sessionInfo['name'] + ); + } + + return empty($this->errorMessages); + } + + /** + * Return true if user is allowed to be added to queue for session subscription. + * + * @param int $userId + * @param array $params MUST have keys: + * "is_connected" Indicate if the user is online on external web + * "profile_completed" Percentage of completed profile, given by WS + * @param bool $collectErrors Optional. Default is false. Whether collect all errors or throw exeptions + * + * @throws Exception + * + * @return bool + */ + public function isAllowedToDoRequest($userId, $params = [], $collectErrors = false) + { + $plugin = self::create(); + $wsUrl = $plugin->get('ws_url'); + // Student always is connected + $isConnected = true; + + if (!$isConnected) { + $this->errorMessages[] = $this->get_lang('AdvancedSubscriptionNotConnected'); + + if (!$collectErrors) { + throw new \Exception($this->get_lang('AdvancedSubscriptionNotConnected')); + } + } + + $profileCompletedMin = (float) $plugin->get('min_profile_percentage'); + $profileCompleted = 0; + + if (is_string($wsUrl) && !empty($wsUrl)) { + $options = [ + 'location' => $wsUrl, + 'uri' => $wsUrl, + ]; + $client = new SoapClient(null, $options); + $userInfo = api_get_user_info($userId); + try { + $profileCompleted = $client->__soapCall('getProfileCompletionPercentage', $userInfo['extra']['drupal_user_id']); + } catch (\Exception $e) { + $profileCompleted = 0; + } + } elseif (isset($params['profile_completed'])) { + $profileCompleted = (float) $params['profile_completed']; + } + + if ($profileCompleted < $profileCompletedMin) { + $errorMessage = sprintf( + $this->get_lang('AdvancedSubscriptionProfileIncomplete'), + $profileCompletedMin, + $profileCompleted + ); + + $this->errorMessages[] = $errorMessage; + + if (!$collectErrors) { + throw new \Exception($errorMessage); + } + } + + $yearlyCostLimit = $plugin->get('yearly_cost_limit'); + $maxCost = $plugin->get('yearly_cost_unit_converter'); + $maxCost *= $yearlyCostLimit; + $userCost = 0; + $now = new DateTime(api_get_utc_datetime()); + $newYearDate = $plugin->get('course_session_credit_year_start_date'); + $newYearDate = !empty($newYearDate) ? + new \DateTime($newYearDate.$now->format('/Y')) : $now; + $extra = new ExtraFieldValue('session'); + $joinSessionTable = Database::get_main_table(TABLE_MAIN_SESSION_USER).' su INNER JOIN '. + Database::get_main_table(TABLE_MAIN_SESSION).' s ON s.id = su.session_id'; + $whereSessionParams = 'su.relation_type = ? AND s.access_start_date >= ? AND su.user_id = ?'; + $whereSessionParamsValues = [ + 0, + $newYearDate->format('Y-m-d'), + $userId, + ]; + $whereSession = [ + 'where' => [ + $whereSessionParams => $whereSessionParamsValues, + ], + ]; + $selectSession = 's.id AS id'; + $sessions = Database::select( + $selectSession, + $joinSessionTable, + $whereSession + ); + + $expendedTimeMax = $plugin->get('yearly_hours_limit'); + $expendedTime = 0; + + if (is_array($sessions) && count($sessions) > 0) { + foreach ($sessions as $session) { + $costField = $extra->get_values_by_handler_and_field_variable($session['id'], 'cost'); + $userCost += $costField['value']; + $teachingHoursField = $extra->get_values_by_handler_and_field_variable($session['id'], 'teaching_hours'); + $expendedTime += $teachingHoursField['value']; + } + } + + if (isset($params['sessionId'])) { + $costField = $extra->get_values_by_handler_and_field_variable($params['sessionId'], 'cost'); + $userCost += $costField['value']; + + $teachingHoursField = $extra->get_values_by_handler_and_field_variable($params['sessionId'], 'teaching_hours'); + $expendedTime += $teachingHoursField['value']; + } + + if ($maxCost <= $userCost) { + $errorMessage = sprintf( + $this->get_lang('AdvancedSubscriptionCostXLimitReached'), + $yearlyCostLimit + ); + + $this->errorMessages[] = $errorMessage; + + if (!$collectErrors) { + throw new \Exception($errorMessage); + } + } + + if ($expendedTimeMax <= $expendedTime) { + $errorMessage = sprintf( + $this->get_lang('AdvancedSubscriptionTimeXLimitReached'), + $expendedTimeMax + ); + + $this->errorMessages[] = $errorMessage; + + if (!$collectErrors) { + throw new \Exception($errorMessage); + } + } + + $expendedNumMax = $plugin->get('courses_count_limit'); + $expendedNum = count($sessions); + + if ($expendedNumMax <= $expendedNum) { + $errorMessage = sprintf( + $this->get_lang('AdvancedSubscriptionCourseXLimitReached'), + $expendedNumMax + ); + + $this->errorMessages[] = $errorMessage; + + if (!$collectErrors) { + throw new \Exception($errorMessage); + } + } + + $checkInduction = $plugin->get('check_induction'); + $numberOfApprovedInductionSessions = $this->getApprovedInductionSessions($userId); + $completedInduction = $numberOfApprovedInductionSessions > 0; + + if ($checkInduction == 'true' && !$completedInduction) { + $this->errorMessages[] = $this->get_lang('AdvancedSubscriptionIncompleteInduction'); + + if (!$collectErrors) { + throw new \Exception($this->get_lang('AdvancedSubscriptionIncompleteInduction')); + } + } + + return empty($this->errorMessages); + } + + /** + * Register a user into a queue for a session. + * + * @param $userId + * @param $sessionId + * + * @return bool|int + */ + public function addToQueue($userId, $sessionId) + { + // Filter input variables + $userId = intval($userId); + $sessionId = intval($sessionId); + $now = api_get_utc_datetime(); + $advancedSubscriptionQueueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $attributes = [ + 'session_id' => $sessionId, + 'user_id' => $userId, + 'status' => 0, + 'created_at' => $now, + 'updated_at' => null, + ]; + + $id = Database::insert($advancedSubscriptionQueueTable, $attributes); + + return $id; + } + + /** + * Register message with type and status. + * + * @param $mailId + * @param $userId + * @param $sessionId + * + * @return bool|int + */ + public function saveLastMessage($mailId, $userId, $sessionId) + { + // Filter variables + $mailId = intval($mailId); + $userId = intval($userId); + $sessionId = intval($sessionId); + $queueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $attributes = [ + 'last_message_id' => $mailId, + 'updated_at' => api_get_utc_datetime(), + ]; + + $num = Database::update( + $queueTable, + $attributes, + ['user_id = ? AND session_id = ?' => [$userId, $sessionId]] + ); + + return $num; + } + + /** + * Check for requirements and register user into queue. + * + * @param $userId + * @param $sessionId + * @param $params + * + * @return bool|string + */ + public function startSubscription($userId, $sessionId, $params) + { + $result = 'Params not found'; + if (!empty($sessionId) && !empty($userId)) { + $plugin = self::create(); + try { + if ($plugin->isAllowedToDoRequest($userId, $params)) { + $result = (bool) $plugin->addToQueue($userId, $sessionId); + } else { + throw new \Exception($this->get_lang('AdvancedSubscriptionNotMoreAble')); + } + } catch (Exception $e) { + $result = $e->getMessage(); + } + } + + return $result; + } + + /** + * Send message for the student subscription approval to a specific session. + * + * @param int|array $studentId + * @param int $receiverId + * @param string $subject + * @param string $content + * @param int $sessionId + * @param bool $save + * @param array $fileAttachments + * + * @return bool|int + */ + public function sendMailMessage( + $studentId, + $receiverId, + $subject, + $content, + $sessionId, + $save = false, + $fileAttachments = [] + ) { + if (!empty($fileAttachments) && + is_array($fileAttachments) && + isset($fileAttachments['files']) && + isset($fileAttachments['comments']) + ) { + $mailId = MessageManager::send_message( + $receiverId, + $subject, + $content, + $fileAttachments['files'], + $fileAttachments['comments'] + ); + } else { + $mailId = MessageManager::send_message( + $receiverId, + $subject, + $content + ); + } + + if ($save && !empty($mailId)) { + // Save as sent message + if (is_array($studentId) && !empty($studentId)) { + foreach ($studentId as $student) { + $this->saveLastMessage($mailId, $student['user_id'], $sessionId); + } + } else { + $studentId = intval($studentId); + $this->saveLastMessage($mailId, $studentId, $sessionId); + } + } elseif (!empty($mailId)) { + // Update queue row, updated_at + Database::update( + Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE), + [ + 'updated_at' => api_get_utc_datetime(), + ], + [ + 'user_id = ? AND session_id = ?' => [$studentId, $sessionId], + ] + ); + } + + return $mailId; + } + + /** + * Check if session is open for subscription. + * + * @param $sessionId + * @param string $fieldVariable + * + * @return bool + */ + public function isSessionOpen($sessionId, $fieldVariable = 'is_open_session') + { + $extraFieldValue = new ExtraFieldValue('session'); + $result = $extraFieldValue->get_values_by_handler_and_field_variable( + $sessionId, + $fieldVariable + ); + + $isOpen = false; + if (!empty($result)) { + $isOpen = (bool) $result['value']; + } + + return $isOpen; + } + + /** + * Check if user is in the session's target group based on its area. + * + * @param $userId + * @param $sessionId + * @param string $userFieldVariable + * @param string $sessionFieldVariable + * + * @return bool + */ + public function isUserInTargetGroup( + $userId, + $sessionId, + $userFieldVariable = 'area', + $sessionFieldVariable = 'target' + ) { + $extraSessionFieldValue = new ExtraFieldValue('session'); + $sessionTarget = $extraSessionFieldValue->get_values_by_handler_and_field_variable( + $sessionId, + $sessionFieldVariable + ); + $extraUserFieldValue = new ExtraFieldValue('user'); + $userArea = $extraUserFieldValue->get_values_by_handler_and_field_variable( + $userId, + $userFieldVariable + ); + $isInTargetGroup = false; + if (isset($sessionTarget) && (!empty($sessionTarget)) && $sessionTarget['value'] == 'minedu') { + if (substr($userArea['value'], 0, 6) == 'MINEDU') { + $isInTargetGroup = true; + } + } + if (isset($sessionTarget) && (!empty($sessionTarget)) && $sessionTarget['value'] == 'regiones') { + if ((substr($userArea['value'], 0, 4) == 'UGEL') || (substr($userArea['value'], 0, 3) == 'DRE')) { + $isInTargetGroup = true; + } + } + + return $isInTargetGroup; + } + + /** + * Update the queue status for subscription approval rejected or accepted. + * + * @param $params + * @param $newStatus + * + * @return bool + */ + public function updateQueueStatus($params, $newStatus) + { + $newStatus = intval($newStatus); + $res = false; + + if (isset($params['queue']['id'])) { + $where = [ + 'id = ?' => intval($params['queue']['id']), + ]; + } elseif (isset($params['studentUserId']) && isset($params['sessionId'])) { + $where = [ + 'user_id = ? AND session_id = ? AND status <> ? AND status <> ?' => [ + intval($params['studentUserId']), + intval($params['sessionId']), + $newStatus, + ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED, + ], + ]; + } + if (isset($where)) { + $res = (bool) Database::update( + Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE), + [ + 'status' => $newStatus, + 'updated_at' => api_get_utc_datetime(), + ], + $where + ); + } + + return $res; + } + + /** + * Render and send mail by defined advanced subscription action. + * + * @param $data + * @param $actionType + * + * @return array + */ + public function sendMail($data, $actionType) + { + $template = new Template($this->get_lang('plugin_title')); + $template->assign('data', $data); + $templateParams = [ + 'user', + 'student', + 'students', + 'superior', + 'admins', + 'session', + 'signature', + 'admin_view_url', + 'acceptUrl', + 'rejectUrl', + ]; + foreach ($templateParams as $templateParam) { + $template->assign($templateParam, $data[$templateParam]); + } + $mailIds = []; + switch ($actionType) { + case ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST: + // Mail to student + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailStudentRequest'), + $template->fetch('/advanced_subscription/views/student_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to superior + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailStudentRequest'), + $template->fetch('/advanced_subscription/views/student_notice_superior.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_APPROVE: + // Mail to student + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailBossAccept'), + $template->fetch('/advanced_subscription/views/superior_accepted_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to superior + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailBossAccept'), + $template->fetch('/advanced_subscription/views/superior_accepted_notice_superior.tpl'), + $data['sessionId'] + ); + // Mail to admin + foreach ($data['admins'] as $adminId => $admin) { + $template->assign('admin', $admin); + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $adminId, + $this->get_lang('MailBossAccept'), + $template->fetch('/advanced_subscription/views/superior_accepted_notice_admin.tpl'), + $data['sessionId'] + ); + } + break; + case ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_DISAPPROVE: + // Mail to student + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailBossReject'), + $template->fetch('/advanced_subscription/views/superior_rejected_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to superior + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailBossReject'), + $template->fetch('/advanced_subscription/views/superior_rejected_notice_superior.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_SUPERIOR_SELECT: + // Mail to student + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailStudentRequestSelect'), + $template->fetch('/advanced_subscription/views/student_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to superior + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailStudentRequestSelect'), + $template->fetch('/advanced_subscription/views/student_notice_superior.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_ADMIN_APPROVE: + $fileAttachments = []; + if (api_get_plugin_setting('courselegal', 'tool_enable')) { + $courseLegal = CourseLegalPlugin::create(); + $courses = SessionManager::get_course_list_by_session_id($data['sessionId']); + $course = current($courses); + $data['courseId'] = $course['id']; + $data['course'] = api_get_course_info_by_id($data['courseId']); + $termsAndConditions = $courseLegal->getData($data['courseId'], $data['sessionId']); + $termsAndConditions = $termsAndConditions['content']; + $termsAndConditions = $this->renderTemplateString($termsAndConditions, $data); + $tpl = new Template(get_lang('TermsAndConditions')); + $tpl->assign('session', $data['session']); + $tpl->assign('student', $data['student']); + $tpl->assign('sessionId', $data['sessionId']); + $tpl->assign('termsContent', $termsAndConditions); + $termsAndConditions = $tpl->fetch('/advanced_subscription/views/terms_and_conditions_to_pdf.tpl'); + $pdf = new PDF(); + $filename = 'terms'.sha1(rand(0, 99999)); + $pdf->content_to_pdf($termsAndConditions, null, $filename, null, 'F'); + $fileAttachments['file'][] = [ + 'name' => $filename.'.pdf', + 'application/pdf' => $filename.'.pdf', + 'tmp_name' => api_get_path(SYS_ARCHIVE_PATH).$filename.'.pdf', + 'error' => UPLOAD_ERR_OK, + 'size' => filesize(api_get_path(SYS_ARCHIVE_PATH).$filename.'.pdf'), + ]; + $fileAttachments['comments'][] = get_lang('TermsAndConditions'); + } + // Mail to student + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailAdminAccept'), + $template->fetch('/advanced_subscription/views/admin_accepted_notice_student.tpl'), + $data['sessionId'], + true, + $fileAttachments + ); + // Mail to superior + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailAdminAccept'), + $template->fetch('/advanced_subscription/views/admin_accepted_notice_superior.tpl'), + $data['sessionId'] + ); + // Mail to admin + $adminId = $data['currentUserId']; + $template->assign('admin', $data['admins'][$adminId]); + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $adminId, + $this->get_lang('MailAdminAccept'), + $template->fetch('/advanced_subscription/views/admin_accepted_notice_admin.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_ADMIN_DISAPPROVE: + // Mail to student + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailAdminReject'), + $template->fetch('/advanced_subscription/views/admin_rejected_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to superior + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $data['superior']['user_id'], + $this->get_lang('MailAdminReject'), + $template->fetch('/advanced_subscription/views/admin_rejected_notice_superior.tpl'), + $data['sessionId'] + ); + // Mail to admin + $adminId = $data['currentUserId']; + $template->assign('admin', $data['admins'][$adminId]); + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $adminId, + $this->get_lang('MailAdminReject'), + $template->fetch('/advanced_subscription/views/admin_rejected_notice_admin.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_STUDENT_REQUEST_NO_BOSS: + // Mail to student + $mailIds['render'] = $this->sendMailMessage( + $data['studentUserId'], + $data['student']['user_id'], + $this->get_lang('MailStudentRequestNoBoss'), + $template->fetch('/advanced_subscription/views/student_no_superior_notice_student.tpl'), + $data['sessionId'], + true + ); + // Mail to admin + foreach ($data['admins'] as $adminId => $admin) { + $template->assign('admin', $admin); + $mailIds[] = $this->sendMailMessage( + $data['studentUserId'], + $adminId, + $this->get_lang('MailStudentRequestNoBoss'), + $template->fetch('/advanced_subscription/views/student_no_superior_notice_admin.tpl'), + $data['sessionId'] + ); + } + break; + case ADVANCED_SUBSCRIPTION_ACTION_REMINDER_STUDENT: + $mailIds['render'] = $this->sendMailMessage( + $data['student']['user_id'], + $data['student']['user_id'], + $this->get_lang('MailRemindStudent'), + $template->fetch('/advanced_subscription/views/reminder_notice_student.tpl'), + $data['sessionId'], + true + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR: + $mailIds['render'] = $this->sendMailMessage( + $data['students'], + $data['superior']['user_id'], + $this->get_lang('MailRemindSuperior'), + $template->fetch('/advanced_subscription/views/reminder_notice_superior.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_REMINDER_SUPERIOR_MAX: + $mailIds['render'] = $this->sendMailMessage( + $data['students'], + $data['superior']['user_id'], + $this->get_lang('MailRemindSuperior'), + $template->fetch('/advanced_subscription/views/reminder_notice_superior_max.tpl'), + $data['sessionId'] + ); + break; + case ADVANCED_SUBSCRIPTION_ACTION_REMINDER_ADMIN: + // Mail to admin + foreach ($data['admins'] as $adminId => $admin) { + $template->assign('admin', $admin); + $mailIds[] = $this->sendMailMessage( + $data['students'], + $adminId, + $this->get_lang('MailRemindAdmin'), + $template->fetch('/advanced_subscription/views/reminder_notice_admin.tpl'), + $data['sessionId'] + ); + } + break; + default: + break; + } + + return $mailIds; + } + + /** + * Count the users in queue filtered by params (sessions, status). + * + * @param array $params Input array containing the set of + * session and status to count from queue + * e.g: + * array('sessions' => array(215, 218, 345, 502), + * 'status' => array(0, 1, 2)) + * + * @return int + */ + public function countQueueByParams($params) + { + $count = 0; + if (!empty($params) && is_array($params)) { + $advancedSubscriptionQueueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $where['1 = ? '] = 1; + if (isset($params['sessions']) && is_array($params['sessions'])) { + foreach ($params['sessions'] as &$sessionId) { + $sessionId = intval($sessionId); + } + $where['AND session_id IN ( ? ) '] = implode($params['sessions']); + } + if (isset($params['status']) && is_array($params['status'])) { + foreach ($params['status'] as &$status) { + $status = intval($status); + } + $where['AND status IN ( ? ) '] = implode($params['status']); + } + $where['where'] = $where; + $count = Database::select('COUNT(*)', $advancedSubscriptionQueueTable, $where); + $count = $count[0]['COUNT(*)']; + } + + return $count; + } + + /** + * This method will call the Hook management insertHook to add Hook observer from this plugin. + */ + public function installHook() + { + $hookObserver = HookAdvancedSubscription::create(); + HookAdminBlock::create()->attach($hookObserver); + HookWSRegistration::create()->attach($hookObserver); + HookNotificationContent::create()->attach($hookObserver); + HookNotificationTitle::create()->attach($hookObserver); + } + + /** + * This method will call the Hook management deleteHook to disable Hook observer from this plugin. + */ + public function uninstallHook() + { + $hookObserver = HookAdvancedSubscription::create(); + HookAdminBlock::create()->detach($hookObserver); + HookWSRegistration::create()->detach($hookObserver); + HookNotificationContent::create()->detach($hookObserver); + HookNotificationTitle::create()->detach($hookObserver); + } + + /** + * Return the status from user in queue to session subscription. + * + * @param int $userId + * @param int $sessionId + * + * @return bool|int + */ + public function getQueueStatus($userId, $sessionId) + { + $userId = intval($userId); + $sessionId = intval($sessionId); + if (!empty($userId) && !empty($sessionId)) { + $queueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $row = Database::select( + 'status', + $queueTable, + [ + 'where' => [ + 'user_id = ? AND session_id = ?' => [$userId, $sessionId], + ], + ] + ); + + if (count($row) == 1) { + return $row[0]['status']; + } else { + return ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE; + } + } + + return false; + } + + /** + * Return the remaining vacancy. + * + * @param $sessionId + * + * @return bool|int + */ + public function getVacancy($sessionId) + { + if (!empty($sessionId)) { + $extra = new ExtraFieldValue('session'); + $var = $extra->get_values_by_handler_and_field_variable( + $sessionId, + 'vacancies' + ); + $vacancy = intval($var['value']); + if (!empty($vacancy)) { + $vacancy -= $this->countQueueByParams( + [ + 'sessions' => [$sessionId], + 'status' => [ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED], + ] + ); + if ($vacancy >= 0) { + return $vacancy; + } else { + return 0; + } + } + } + + return false; + } + + /** + * Return the session details data from a session ID (including the extra + * fields used for the advanced subscription mechanism). + * + * @param $sessionId + * + * @return bool|mixed + */ + public function getSessionDetails($sessionId) + { + if (!empty($sessionId)) { + // Assign variables + $fieldsArray = [ + 'code', + 'cost', + 'place', + 'allow_visitors', + 'teaching_hours', + 'brochure', + 'banner', + ]; + $extraField = new ExtraField('session'); + // Get session fields + $fieldList = $extraField->get_all([ + 'variable IN ( ?, ?, ?, ?, ?, ?, ? )' => $fieldsArray, + ]); + // Index session fields + $fields = []; + foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; + } + + $mergedArray = array_merge([$sessionId], array_keys($fields)); + + $sql = "SELECT * FROM ".Database::get_main_table(TABLE_EXTRA_FIELD_VALUES)." + WHERE item_id = %d AND field_id IN (%d, %d, %d, %d, %d, %d, %d)"; + $sql = vsprintf($sql, $mergedArray); + $sessionFieldValueList = Database::query($sql); + while ($sessionFieldValue = Database::fetch_assoc($sessionFieldValueList)) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } + } + $sessionArray['description'] = SessionManager::getDescriptionFromSessionId($sessionId); + + if (isset($sessionArray['brochure'])) { + $sessionArray['brochure'] = api_get_path(WEB_UPLOAD_PATH).$sessionArray['brochure']; + } + if (isset($sessionArray['banner'])) { + $sessionArray['banner'] = api_get_path(WEB_UPLOAD_PATH).$sessionArray['banner']; + } + + return $sessionArray; + } + + return false; + } + + /** + * Get status message. + * + * @param int $status + * @param bool $isAble + * + * @return string + */ + public function getStatusMessage($status, $isAble = true) + { + switch ($status) { + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE: + $message = $this->get_lang('AdvancedSubscriptionNoQueue'); + if ($isAble) { + $message = $this->get_lang('AdvancedSubscriptionNoQueueIsAble'); + } + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START: + $message = $this->get_lang('AdvancedSubscriptionQueueStart'); + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED: + $message = $this->get_lang('AdvancedSubscriptionQueueBossDisapproved'); + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED: + $message = $this->get_lang('AdvancedSubscriptionQueueBossApproved'); + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED: + $message = $this->get_lang('AdvancedSubscriptionQueueAdminDisapproved'); + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED: + $message = $this->get_lang('AdvancedSubscriptionQueueAdminApproved'); + break; + default: + $message = sprintf($this->get_lang('AdvancedSubscriptionQueueDefault'), $status); + } + + return $message; + } + + /** + * Return the url to go to session. + * + * @param $sessionId + * + * @return string + */ + public function getSessionUrl($sessionId) + { + $url = api_get_path(WEB_CODE_PATH).'session/?session_id='.intval($sessionId); + + return $url; + } + + /** + * Get a url for subscribe a user in session. + * + * @param int $userId The user ID + * @param array $params Params from WS + * + * @return string + */ + public function getOpenSessionUrl($userId, $params) + { + $userIsSubscribed = SessionManager::isUserSubscribedAsStudent( + $params['session_id'], + $userId + ); + + if ($userIsSubscribed) { + return api_get_path(WEB_CODE_PATH) + .'session/index.php?session_id=' + .intval($params['session_id']); + } + + $params['secret_key'] = null; + $params['user_id'] = null; + $params['user_field'] = null; + $params['is_connected'] = null; + + $urlParams = array_merge($params, ['user_id' => $userId]); + + $url = api_get_path(WEB_PLUGIN_PATH); + $url .= 'advanced_subscription/src/open_session.php?'; + $url .= http_build_query($urlParams); + + return 'javascript:void(window.open(\'' + .$url + .'\',\'AdvancedSubscriptionTerms\', \'toolbar=no,location=no,' + .'status=no,menubar=no,scrollbars=yes,resizable=yes,width=700px,' + .'height=600px\', \'100\' ))'; + } + + /** + * Return the url to enter to subscription queue to session. + * + * @param $params + * + * @return string + */ + public function getQueueUrl($params) + { + $url = api_get_path(WEB_PLUGIN_PATH).'advanced_subscription/ajax/advanced_subscription.ajax.php?'. + 'a='.Security::remove_XSS($params['action']).'&'. + 's='.intval($params['sessionId']).'&'. + 'current_user_id='.intval($params['currentUserId']).'&'. + 'e='.intval($params['newStatus']).'&'. + 'u='.intval($params['studentUserId']).'&'. + 'q='.intval($params['queueId']).'&'. + 'is_connected=1&'. + 'profile_completed='.intval($params['profile_completed']).'&'. + 'v='.$this->generateHash($params); + + return $url; + } + + /** + * Return the list of student, in queue used by admin view. + * + * @param int $sessionId + * + * @return array + */ + public function listAllStudentsInQueueBySession($sessionId) + { + // Filter input variable + $sessionId = intval($sessionId); + // Assign variables + $fieldsArray = [ + 'target', + 'publication_end_date', + 'mode', + 'recommended_number_of_participants', + 'vacancies', + ]; + $sessionArray = api_get_session_info($sessionId); + $extraSession = new ExtraFieldValue('session'); + $extraField = new ExtraField('session'); + // Get session fields + $fieldList = $extraField->get_all([ + 'variable IN ( ?, ?, ?, ?, ?)' => $fieldsArray, + ]); + // Index session fields + $fields = []; + foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; + } + + $mergedArray = array_merge([$sessionId], array_keys($fields)); + $sessionFieldValueList = $extraSession->get_all( + [ + 'item_id = ? field_id IN ( ?, ?, ?, ?, ?, ?, ? )' => $mergedArray, + ] + ); + foreach ($sessionFieldValueList as $sessionFieldValue) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } + } + $queueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $userJoinTable = $queueTable.' q INNER JOIN '.$userTable.' u ON q.user_id = u.user_id'; + $where = [ + 'where' => [ + 'q.session_id = ?' => [ + $sessionId, + ], + ], + 'order' => 'qstatus DESC, lastname ASC', + ]; + $select = 'u.user_id, u.firstname, u.lastname, q.created_at, q.updated_at, q.status as qstatus, q.id as queue_id'; + $students = Database::select($select, $userJoinTable, $where); + foreach ($students as &$student) { + $status = intval($student['qstatus']); + switch ($status) { + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE: + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START: + $student['validation'] = ''; + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED: + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED: + $student['validation'] = 'No'; + break; + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED: + case ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED: + $student['validation'] = 'Yes'; + break; + default: + error_log(__FILE__.' '.__FUNCTION__.' Student status no detected'); + } + } + $return = [ + 'session' => $sessionArray, + 'students' => $students, + ]; + + return $return; + } + + /** + * List all session (id, name) for select input. + * + * @param int $limit + * + * @return array + */ + public function listAllSessions($limit = 100) + { + $limit = intval($limit); + $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); + $columns = 'id, name'; + $conditions = []; + if ($limit > 0) { + $conditions = [ + 'order' => 'name', + 'limit' => $limit, + ]; + } + + return Database::select($columns, $sessionTable, $conditions); + } + + /** + * Generate security hash to check data send by url params. + * + * @param string $data + * + * @return string + */ + public function generateHash($data) + { + $key = sha1($this->get('secret_key')); + // Prepare array to have specific type variables + $dataPrepared['action'] = strval($data['action']); + $dataPrepared['sessionId'] = intval($data['sessionId']); + $dataPrepared['currentUserId'] = intval($data['currentUserId']); + $dataPrepared['studentUserId'] = intval($data['studentUserId']); + $dataPrepared['queueId'] = intval($data['queueId']); + $dataPrepared['newStatus'] = intval($data['newStatus']); + $dataPrepared = serialize($dataPrepared); + + return sha1($dataPrepared.$key); + } + + /** + * Verify hash from data. + * + * @param string $data + * @param string $hash + * + * @return bool + */ + public function checkHash($data, $hash) + { + return $this->generateHash($data) == $hash; + } + + /** + * Copied and fixed from plugin.class.php + * Returns the "system" name of the plugin in lowercase letters. + * + * @return string + */ + public function get_name() + { + return 'advanced_subscription'; + } + + /** + * Return the url to show subscription terms. + * + * @param array $params + * @param int $mode + * + * @return string + */ + public function getTermsUrl($params, $mode = ADVANCED_SUBSCRIPTION_TERMS_MODE_POPUP) + { + $urlParams = [ + 'a' => Security::remove_XSS($params['action']), + 's' => intval($params['sessionId']), + 'current_user_id' => intval($params['currentUserId']), + 'e' => intval($params['newStatus']), + 'u' => intval($params['studentUserId']), + 'q' => intval($params['queueId']), + 'is_connected' => 1, + 'profile_completed' => intval($params['profile_completed']), + 'v' => $this->generateHash($params), + ]; + + switch ($mode) { + case ADVANCED_SUBSCRIPTION_TERMS_MODE_POPUP: + case ADVANCED_SUBSCRIPTION_TERMS_MODE_FINAL: + $urlParams['r'] = 0; + break; + case ADVANCED_SUBSCRIPTION_TERMS_MODE_REJECT: + $urlParams['r'] = 1; + break; + } + + $url = api_get_path(WEB_PLUGIN_PATH)."advanced_subscription/src/terms_and_conditions.php?"; + $url .= http_build_query($urlParams); + + // Launch popup + if ($mode == ADVANCED_SUBSCRIPTION_TERMS_MODE_POPUP) { + $url = 'javascript:void(window.open(\''.$url.'\',\'AdvancedSubscriptionTerms\', \'toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=700px,height=600px\', \'100\' ))'; + } + + return $url; + } + + /** + * Return the url to get mail rendered. + * + * @param array $params + * + * @return string + */ + public function getRenderMailUrl($params) + { + $url = api_get_path(WEB_PLUGIN_PATH).'advanced_subscription/src/render_mail.php?'. + 'q='.$params['queueId'].'&'. + 'v='.$this->generateHash($params); + + return $url; + } + + /** + * Return the last message id from queue row. + * + * @param int $studentUserId + * @param int $sessionId + * + * @return int|bool + */ + public function getLastMessageId($studentUserId, $sessionId) + { + $studentUserId = intval($studentUserId); + $sessionId = intval($sessionId); + if (!empty($sessionId) && !empty($studentUserId)) { + $row = Database::select( + 'last_message_id', + Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE), + [ + 'where' => [ + 'user_id = ? AND session_id = ?' => [$studentUserId, $sessionId], + ], + ] + ); + + if (count($row) > 0) { + return $row[0]['last_message_id']; + } + } + + return false; + } + + /** + * Return string replacing tags "{{}}"with variables assigned in $data. + * + * @param string $templateContent + * @param array $data + * + * @return string + */ + public function renderTemplateString($templateContent, $data = []) + { + $twigString = new \Twig_Environment(new \Twig_Loader_String()); + $templateContent = $twigString->render( + $templateContent, + $data + ); + + return $templateContent; + } + + /** + * addAreaField() (adds an area field if it is not already created). + */ + private function addAreaField() + { + $extraField = new ExtraField('user'); + $extraFieldHandler = $extraField->get_handler_field_info_by_field_variable('area'); + $areaExists = $extraFieldHandler !== false; + + if (!$areaExists) { + $extraField = new ExtraField('user'); + $extraField->save([ + 'field_type' => 1, + 'variable' => 'area', + 'display_text' => get_plugin_lang('Area', 'AdvancedSubscriptionPlugin'), + 'default_value' => null, + 'field_order' => null, + 'visible_to_self' => 1, + 'changeable' => 1, + 'filter' => null, + ]); + } + } + + /** + * Create the database tables for the plugin. + */ + private function installDatabase() + { + $advancedSubscriptionQueueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + + $sql = "CREATE TABLE IF NOT EXISTS $advancedSubscriptionQueueTable (". + "id int UNSIGNED NOT NULL AUTO_INCREMENT, ". + "session_id int UNSIGNED NOT NULL, ". + "user_id int UNSIGNED NOT NULL, ". + "status int UNSIGNED NOT NULL, ". + "last_message_id int UNSIGNED NOT NULL, ". + "created_at datetime NOT NULL, ". + "updated_at datetime NULL, ". + "PRIMARY KEY PK_advanced_subscription_queue (id), ". + "UNIQUE KEY UK_advanced_subscription_queue (user_id, session_id)); "; + Database::query($sql); + } + + /** + * Drop the database tables for the plugin. + */ + private function uninstallDatabase() + { + /* Drop plugin tables */ + $advancedSubscriptionQueueTable = Database::get_main_table(TABLE_ADVANCED_SUBSCRIPTION_QUEUE); + $sql = "DROP TABLE IF EXISTS $advancedSubscriptionQueueTable; "; + Database::query($sql); + + /* Delete settings */ + $settingsTable = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); + Database::query("DELETE FROM $settingsTable WHERE subkey = 'advanced_subscription'"); + } + + /** + * Get the count of approved induction sessions by a user. + * + * @param int $userId The user id + * + * @return int The count of approved sessions + */ + private function getApprovedInductionSessions($userId) + { + $tSession = Database::get_main_table(TABLE_MAIN_SESSION); + $tSessionField = Database::get_main_table(TABLE_EXTRA_FIELD); + $tSessionFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + $tSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER); + $extraFieldType = \Chamilo\CoreBundle\Entity\ExtraField::SESSION_FIELD_TYPE; + $sql = "SELECT s.id FROM $tSession AS s + INNER JOIN $tSessionFieldValues AS sfv ON s.id = sfv.item_id + INNER JOIN $tSessionField AS sf ON sfv.field_id = sf.id + INNER JOIN $tSessionUser AS su ON s.id = su.session_id + WHERE + sf.extra_field_type = $extraFieldType AND + sf.variable = 'is_induction_session' AND + su.relation_type = 0 AND + su.user_id = ".intval($userId); + + $result = Database::query($sql); + + if ($result === false) { + return 0; + } + + $numberOfApproved = 0; + + while ($session = Database::fetch_assoc($result)) { + $numberOfApprovedCourses = 0; + $courses = SessionManager::get_course_list_by_session_id($session['id']); + + foreach ($courses as $course) { + $courseCategories = Category::load( + null, + null, + $course['code'], + null, + null, + $session['id'], + false + ); + + if (count($courseCategories) > 0 && + Category::userFinishedCourse($userId, $courseCategories[0]) + ) { + $numberOfApprovedCourses++; + } + } + + if ($numberOfApprovedCourses === count($courses)) { + $numberOfApproved++; + } + } + + return $numberOfApproved; + } +} diff --git a/plugin/advanced_subscription/src/HookAdvancedSubscription.php b/plugin/advanced_subscription/src/HookAdvancedSubscription.php new file mode 100644 index 000000000..db89bcf64 --- /dev/null +++ b/plugin/advanced_subscription/src/HookAdvancedSubscription.php @@ -0,0 +1,681 @@ + + * + * @package chamilo.plugin.advanced_subscription + */ +require_once __DIR__.'/../config.php'; + +/** + * Class HookAdvancedSubscription extends the HookObserver to implements + * specific behaviour when the AdvancedSubscription plugin is enabled. + */ +class HookAdvancedSubscription extends HookObserver implements HookAdminBlockObserverInterface, HookWSRegistrationObserverInterface, HookNotificationContentObserverInterface +{ + public static $plugin; + + /** + * Constructor. Calls parent, mainly. + */ + protected function __construct() + { + self::$plugin = AdvancedSubscriptionPlugin::create(); + parent::__construct( + 'plugin/advanced_subscription/src/HookAdvancedSubscription.class.php', + 'advanced_subscription' + ); + } + + /** + * @return array + */ + public function hookAdminBlock(HookAdminBlockEventInterface $hook) + { + $data = $hook->getEventData(); + // if ($data['type'] === HOOK_EVENT_TYPE_PRE) // Nothing to do + if ($data['type'] === HOOK_EVENT_TYPE_POST) { + if (isset($data['blocks'])) { + $data['blocks']['sessions']['items'][] = [ + 'url' => '../../plugin/advanced_subscription/src/admin_view.php', + 'label' => get_plugin_lang('plugin_title', 'AdvancedSubscriptionPlugin'), + ]; + } + } // Else: Hook type is not valid, nothing to do + + return $data; + } + + /** + * Add Webservices to registration.soap.php. + * + * @return mixed (int or false) + */ + public function hookWSRegistration(HookWSRegistrationEventInterface $hook) + { + $data = $hook->getEventData(); + //if ($data['type'] === HOOK_EVENT_TYPE_PRE) // nothing to do + if ($data['type'] === HOOK_EVENT_TYPE_POST) { + /** @var \nusoap_server $server */ + $server = &$data['server']; + + /** WSSessionListInCategory */ + + // Output params for sessionBriefList WSSessionListInCategory + $server->wsdl->addComplexType( + 'sessionBrief', + 'complexType', + 'struct', + 'all', + '', + [ + // session.id + 'id' => ['name' => 'id', 'type' => 'xsd:int'], + // session.name + 'name' => ['name' => 'name', 'type' => 'xsd:string'], + // session.short_description + 'short_description' => ['name' => 'short_description', 'type' => 'xsd:string'], + // session.mode + 'mode' => ['name' => 'mode', 'type' => 'xsd:string'], + // session.date_start + 'date_start' => ['name' => 'date_start', 'type' => 'xsd:string'], + // session.date_end + 'date_end' => ['name' => 'date_end', 'type' => 'xsd:string'], + // session.human_text_duration + 'human_text_duration' => ['name' => 'human_text_duration', 'type' => 'xsd:string'], + // session.vacancies + 'vacancies' => ['name' => 'vacancies', 'type' => 'xsd:string'], + // session.schedule + 'schedule' => ['name' => 'schedule', 'type' => 'xsd:string'], + ] + ); + + //Output params for WSSessionListInCategory + $server->wsdl->addComplexType( + 'sessionBriefList', + 'complexType', + 'array', + '', + 'SOAP-ENC:Array', + [], + [ + ['ref' => 'SOAP-ENC:arrayType', + 'wsdl:arrayType' => 'tns:sessionBrief[]', ], + ], + 'tns:sessionBrief' + ); + + // Input params for WSSessionListInCategory + $server->wsdl->addComplexType( + 'sessionCategoryInput', + 'complexType', + 'struct', + 'all', + '', + [ + 'id' => ['name' => 'id', 'type' => 'xsd:string'], // session_category.id + 'name' => ['name' => 'name', 'type' => 'xsd:string'], // session_category.name + 'target' => ['name' => 'target', 'type' => 'xsd:string'], // session.target + 'secret_key' => ['name' => 'secret_key', 'type' => 'xsd:string'], + ] + ); + + // Input params for WSSessionGetDetailsByUser + $server->wsdl->addComplexType( + 'advsubSessionDetailInput', + 'complexType', + 'struct', + 'all', + '', + [ + // user_field_values.value + 'user_id' => ['name' => 'user_id', 'type' => 'xsd:int'], + // user_field.user_id + 'user_field' => ['name' => 'user_field', 'type' => 'xsd:string'], + // session.id + 'session_id' => ['name' => 'session_id', 'type' => 'xsd:int'], + // user.profile_completes + 'profile_completed' => ['name' => 'profile_completed', 'type' => 'xsd:float'], + // user.is_connected + 'is_connected' => ['name' => 'is_connected', 'type' => 'xsd:boolean'], + 'secret_key' => ['name' => 'secret_key', 'type' => 'xsd:string'], + ] + ); + + // Output params for WSSessionGetDetailsByUser + $server->wsdl->addComplexType( + 'advsubSessionDetail', + 'complexType', + 'struct', + 'all', + '', + [ + // session.id + 'id' => ['name' => 'id', 'type' => 'xsd:string'], + // session.code + 'code' => ['name' => 'code', 'type' => 'xsd:string'], + // session.place + 'cost' => ['name' => 'cost', 'type' => 'xsd:float'], + // session.place + 'place' => ['name' => 'place', 'type' => 'xsd:string'], + // session.allow_visitors + 'allow_visitors' => ['name' => 'allow_visitors', 'type' => 'xsd:string'], + // session.teaching_hours + 'teaching_hours' => ['name' => 'teaching_hours', 'type' => 'xsd:int'], + // session.brochure + 'brochure' => ['name' => 'brochure', 'type' => 'xsd:string'], + // session.banner + 'banner' => ['name' => 'banner', 'type' => 'xsd:string'], + // session.description + 'description' => ['name' => 'description', 'type' => 'xsd:string'], + // status + 'status' => ['name' => 'status', 'type' => 'xsd:string'], + // action_url + 'action_url' => ['name' => 'action_url', 'type' => 'xsd:string'], + // message + 'message' => ['name' => 'error_message', 'type' => 'xsd:string'], + ] + ); + + /** WSListSessionsDetailsByCategory */ + + // Input params for WSListSessionsDetailsByCategory + $server->wsdl->addComplexType( + 'listSessionsDetailsByCategory', + 'complexType', + 'struct', + 'all', + '', + [ + // session_category.id + 'id' => ['name' => 'id', 'type' => 'xsd:string'], + // session_category.access_url_id + 'access_url_id' => ['name' => 'access_url_id', 'type' => 'xsd:int'], + // session_category.name + 'category_name' => ['name' => 'category_name', 'type' => 'xsd:string'], + // secret key + 'secret_key' => ['name' => 'secret_key', 'type' => 'xsd:string'], + ], + [], + 'tns:listSessionsDetailsByCategory' + ); + + // Output params for sessionDetailsCourseList WSListSessionsDetailsByCategory + $server->wsdl->addComplexType( + 'sessionDetailsCourse', + 'complexType', + 'struct', + 'all', + '', + [ + 'course_id' => ['name' => 'course_id', 'type' => 'xsd:int'], // course.id + 'course_code' => ['name' => 'course_code', 'type' => 'xsd:string'], // course.code + 'course_title' => ['name' => 'course_title', 'type' => 'xsd:string'], // course.title + 'coach_username' => ['name' => 'coach_username', 'type' => 'xsd:string'], // user.username + 'coach_firstname' => ['name' => 'coach_firstname', 'type' => 'xsd:string'], // user.firstname + 'coach_lastname' => ['name' => 'coach_lastname', 'type' => 'xsd:string'], // user.lastname + ] + ); + + // Output array for sessionDetails WSListSessionsDetailsByCategory + $server->wsdl->addComplexType( + 'sessionDetailsCourseList', + 'complexType', + 'array', + '', + 'SOAP-ENC:Array', + [], + [ + [ + 'ref' => 'SOAP-ENC:arrayType', + 'wsdl:arrayType' => 'tns:sessionDetailsCourse[]', + ], + ], + 'tns:sessionDetailsCourse' + ); + + // Output params for sessionDetailsList WSListSessionsDetailsByCategory + $server->wsdl->addComplexType( + 'sessionDetails', + 'complexType', + 'struct', + 'all', + '', + [ + // session.id + 'id' => [ + 'name' => 'id', + 'type' => 'xsd:int', + ], + // session.id_coach + 'coach_id' => [ + 'name' => 'coach_id', + 'type' => 'xsd:int', + ], + // session.name + 'name' => [ + 'name' => 'name', + 'type' => 'xsd:string', + ], + // session.nbr_courses + 'courses_num' => [ + 'name' => 'courses_num', + 'type' => 'xsd:int', + ], + // session.nbr_users + 'users_num' => [ + 'name' => 'users_num', + 'type' => 'xsd:int', + ], + // session.nbr_classes + 'classes_num' => [ + 'name' => 'classes_num', + 'type' => 'xsd:int', + ], + // session.date_start + 'date_start' => [ + 'name' => 'date_start', + 'type' => 'xsd:string', + ], + // session.date_end + 'date_end' => [ + 'name' => 'date_end', + 'type' => 'xsd:string', + ], + // session.nb_days_access_before_beginning + 'access_days_before_num' => [ + 'name' => 'access_days_before_num', + 'type' => 'xsd:int', + ], + // session.nb_days_access_after_end + 'access_days_after_num' => [ + 'name' => 'access_days_after_num', + 'type' => 'xsd:int', + ], + // session.session_admin_id + 'session_admin_id' => [ + 'name' => 'session_admin_id', + 'type' => 'xsd:int', + ], + // session.visibility + 'visibility' => [ + 'name' => 'visibility', + 'type' => 'xsd:int', + ], + // session.session_category_id + 'session_category_id' => [ + 'name' => 'session_category_id', + 'type' => 'xsd:int', + ], + // session.promotion_id + 'promotion_id' => [ + 'name' => 'promotion_id', + 'type' => 'xsd:int', + ], + // session.number of registered users validated + 'validated_user_num' => [ + 'name' => 'validated_user_num', + 'type' => 'xsd:int', + ], + // session.number of registered users from waiting queue + 'waiting_user_num' => [ + 'name' => 'waiting_user_num', + 'type' => 'xsd:int', + ], + // extra fields + // Array(field_name, field_value) + 'extra' => [ + 'name' => 'extra', + 'type' => 'tns:extrasList', + ], + // course and coaches data + // Array(course_id, course_code, course_title, coach_username, coach_firstname, coach_lastname) + 'course' => [ + 'name' => 'courses', + 'type' => 'tns:sessionDetailsCourseList', + ], + ] + ); + + // Output params for WSListSessionsDetailsByCategory + $server->wsdl->addComplexType( + 'sessionDetailsList', + 'complexType', + 'array', + '', + 'SOAP-ENC:Array', + [], + [ + [ + 'ref' => 'SOAP-ENC:arrayType', + 'wsdl:arrayType' => 'tns:sessionDetails[]', + ], + ], + 'tns:sessionDetails' + ); + + // Register the method for WSSessionListInCategory + $server->register( + 'HookAdvancedSubscription..WSSessionListInCategory', // method name + ['sessionCategoryInput' => 'tns:sessionCategoryInput'], // input parameters + ['return' => 'tns:sessionBriefList'], // output parameters + 'urn:WSRegistration', // namespace + 'urn:WSRegistration#WSSessionListInCategory', // soapaction + 'rpc', // style + 'encoded', // use + 'This service checks if user assigned to course' // documentation + ); + + // Register the method for WSSessionGetDetailsByUser + $server->register( + 'HookAdvancedSubscription..WSSessionGetDetailsByUser', // method name + ['advsubSessionDetailInput' => 'tns:advsubSessionDetailInput'], // input parameters + ['return' => 'tns:advsubSessionDetail'], // output parameters + 'urn:WSRegistration', // namespace + 'urn:WSRegistration#WSSessionGetDetailsByUser', // soapaction + 'rpc', // style + 'encoded', // use + 'This service return session details to specific user' // documentation + ); + + // Register the method for WSListSessionsDetailsByCategory + $server->register( + 'HookAdvancedSubscription..WSListSessionsDetailsByCategory', // method name + ['name' => 'tns:listSessionsDetailsByCategory'], // input parameters + ['return' => 'tns:sessionDetailsList'], // output parameters + 'urn:WSRegistration', // namespace + 'urn:WSRegistration#WSListSessionsDetailsByCategory', // soapaction + 'rpc', // style + 'encoded', // use + 'This service returns a list of detailed sessions by a category' // documentation + ); + + return $data; + } // Else: Nothing to do + + return false; + } + + /** + * @param $params + * + * @return soap_fault|null + */ + public static function WSSessionListInCategory($params) + { + global $debug; + + if ($debug) { + error_log(__FUNCTION__); + error_log('Params '.print_r($params, 1)); + if (!WSHelperVerifyKey($params)) { + error_log(return_error(WS_ERROR_SECRET_KEY)); + } + } + // Check if category ID is set + if (!empty($params['id']) && empty($params['name'])) { + $sessionCategoryId = $params['id']; + } elseif (!empty($params['name'])) { + // Check if category name is set + $sessionCategoryId = SessionManager::getSessionCategoryIdByName($params['name']); + if (is_array($sessionCategoryId)) { + $sessionCategoryId = current($sessionCategoryId); + } + } else { + // Return soap fault Not valid input params + + return return_error(WS_ERROR_INVALID_INPUT); + } + + // Get the session brief List by category + $fields = [ + 'id', + 'short_description', + 'mode', + 'human_text_duration', + 'vacancies', + 'schedule', + ]; + $datePub = new DateTime(); + $sessionList = SessionManager::getShortSessionListAndExtraByCategory( + $sessionCategoryId, + $params['target'], + $fields, + $datePub + ); + + return $sessionList; + } + + /** + * @param $params + * + * @return soap_fault|null + */ + public static function WSSessionGetDetailsByUser($params) + { + global $debug; + + if ($debug) { + error_log('WSUserSubscribedInCourse'); + error_log('Params '.print_r($params, 1)); + } + if (!WSHelperVerifyKey($params)) { + return return_error(WS_ERROR_SECRET_KEY); + } + // Check params + if (is_array($params) && !empty($params['session_id']) && !empty($params['user_id'])) { + $userId = UserManager::get_user_id_from_original_id($params['user_id'], $params['user_field']); + $sessionId = (int) $params['session_id']; + // Check if user exists + if (UserManager::is_user_id_valid($userId) && + SessionManager::isValidId($sessionId) + ) { + // Check if student is already subscribed + $plugin = AdvancedSubscriptionPlugin::create(); + $isOpen = $plugin->isSessionOpen($sessionId); + $status = $plugin->getQueueStatus($userId, $sessionId); + $vacancy = $plugin->getVacancy($sessionId); + $data = $plugin->getSessionDetails($sessionId); + $isUserInTargetGroup = $plugin->isUserInTargetGroup($userId, $sessionId); + if (!empty($data) && is_array($data)) { + $data['status'] = $status; + // Vacancy and queue status cases: + if ($isOpen) { + // Go to Course session + $data['action_url'] = self::$plugin->getOpenSessionUrl($userId, $params); + if (SessionManager::isUserSubscribedAsStudent($sessionId, $userId)) { + $data['status'] = 10; + } + } else { + if (!$isUserInTargetGroup) { + $data['status'] = -2; + } else { + try { + $isAllowed = self::$plugin->isAllowedToDoRequest($userId, $params); + $data['message'] = self::$plugin->getStatusMessage($status, $isAllowed); + } catch (\Exception $e) { + $data['message'] = $e->getMessage(); + } + $params['action'] = 'subscribe'; + $params['sessionId'] = intval($sessionId); + $params['currentUserId'] = 0; // No needed + $params['studentUserId'] = intval($userId); + $params['queueId'] = 0; // No needed + $params['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START; + if ($vacancy > 0) { + // Check conditions + if ($status == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE) { + // No in Queue, require queue subscription url action + $data['action_url'] = self::$plugin->getTermsUrl($params); + } elseif ($status == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED) { + // send url action + $data['action_url'] = self::$plugin->getSessionUrl($sessionId); + } // Else: In queue, output status message, no more info. + } else { + if ($status == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED) { + $data['action_url'] = self::$plugin->getSessionUrl($sessionId); + } elseif ($status == ADVANCED_SUBSCRIPTION_QUEUE_STATUS_NO_QUEUE) { + // in Queue or not, cannot be subscribed to session + $data['action_url'] = self::$plugin->getTermsUrl($params); + } // Else: In queue, output status message, no more info. + } + } + } + $result = $data; + } else { + // Return soap fault No result was found + $result = return_error(WS_ERROR_NOT_FOUND_RESULT); + } + } else { + // Return soap fault No result was found + $result = return_error(WS_ERROR_NOT_FOUND_RESULT); + } + } else { + // Return soap fault Not valid input params + $result = return_error(WS_ERROR_INVALID_INPUT); + } + + return $result; + } + + /** + * Get a list of sessions (id, coach_id, name, courses_num, users_num, classes_num, + * access_start_date, access_end_date, access_days_before_num, session_admin_id, visibility, + * session_category_id, promotion_id, + * validated_user_num, waiting_user_num, + * extra, course) the validated_usernum and waiting_user_num are + * used when have the plugin for advance incsription enables. + * The extra data (field_name, field_value) + * The course data (course_id, course_code, course_title, + * coach_username, coach_firstname, coach_lastname). + * + * @param array $params List of parameters (id, category_name, access_url_id, secret_key) + * + * @return array|soap_fault Sessions list (id=>[title=>'title',url='http://...',date_start=>'...',date_end=>'']) + */ + public static function WSListSessionsDetailsByCategory($params) + { + global $debug; + + if ($debug) { + error_log('WSListSessionsDetailsByCategory'); + error_log('Params '.print_r($params, 1)); + } + $secretKey = $params['secret_key']; + + // Check if secret key is valid + if (!WSHelperVerifyKey($secretKey)) { + return return_error(WS_ERROR_SECRET_KEY); + } + + // Check if category ID is set + if (!empty($params['id']) && empty($params['category_name'])) { + $sessionCategoryId = $params['id']; + } elseif (!empty($params['category_name'])) { + // Check if category name is set + $sessionCategoryId = SessionManager::getSessionCategoryIdByName($params['category_name']); + if (is_array($sessionCategoryId)) { + $sessionCategoryId = current($sessionCategoryId); + } + } else { + // Return soap fault Not valid input params + + return return_error(WS_ERROR_INVALID_INPUT); + } + + // Get the session List by category + $sessionList = SessionManager::getSessionListAndExtraByCategoryId($sessionCategoryId); + + if (empty($sessionList)) { + // If not found any session, return error + + return return_error(WS_ERROR_NOT_FOUND_RESULT); + } + + // Get validated and waiting queue users count for each session + AdvancedSubscriptionPlugin::create(); + foreach ($sessionList as &$session) { + // Add validated and queue users count + $session['validated_user_num'] = self::$plugin->countQueueByParams( + [ + 'sessions' => [$session['id']], + 'status' => [ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED], + ] + ); + $session['waiting_user_num'] = self::$plugin->countQueueByParams( + [ + 'sessions' => [$session['id']], + 'status' => [ + ADVANCED_SUBSCRIPTION_QUEUE_STATUS_START, + ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED, + ], + ] + ); + } + + return $sessionList; + } + + /** + * Return notification content when the hook has been triggered. + * + * @return mixed (int or false) + */ + public function hookNotificationContent(HookNotificationContentEventInterface $hook) + { + $data = $hook->getEventData(); + if ($data['type'] === HOOK_EVENT_TYPE_PRE) { + $data['advanced_subscription_pre_content'] = $data['content']; + + return $data; + } elseif ($data['type'] === HOOK_EVENT_TYPE_POST) { + if (isset($data['content']) && + !empty($data['content']) && + isset($data['advanced_subscription_pre_content']) && + !empty($data['advanced_subscription_pre_content']) + ) { + $data['content'] = str_replace( + [ + '

', + '
', + '
', + ], + '', + $data['advanced_subscription_pre_content'] + ); + } + + return $data; + } //Else hook type is not valid, nothing to do + + return false; + } + + /** + * Return the notification data title if the hook was triggered. + * + * @return array|bool + */ + public function hookNotificationTitle(HookNotificationTitleEventInterface $hook) + { + $data = $hook->getEventData(); + if ($data['type'] === HOOK_EVENT_TYPE_PRE) { + $data['advanced_subscription_pre_title'] = $data['title']; + + return $data; + } elseif ($data['type'] === HOOK_EVENT_TYPE_POST) { + if (isset($data['advanced_subscription_pre_title']) && + !empty($data['advanced_subscription_pre_title']) + ) { + $data['title'] = $data['advanced_subscription_pre_title']; + } + + return $data; + } // Else: hook type is not valid, nothing to do + + return false; + } +} diff --git a/plugin/advanced_subscription/src/admin_view.php b/plugin/advanced_subscription/src/admin_view.php new file mode 100644 index 000000000..62f823ed9 --- /dev/null +++ b/plugin/advanced_subscription/src/admin_view.php @@ -0,0 +1,104 @@ +get_lang('plugin_title')); +// Get all sessions +$sessionList = $plugin->listAllSessions(); + +if (!empty($sessionId)) { + // Get student list in queue + $studentList = $plugin->listAllStudentsInQueueBySession($sessionId); + // Set selected to current session + $sessionList[$sessionId]['selected'] = 'selected="selected"'; + $studentList['session']['id'] = $sessionId; + // Assign variables + $fieldsArray = [ + 'description', + 'target', + 'mode', + 'publication_end_date', + 'recommended_number_of_participants', + 'vacancies', + ]; + $sessionArray = api_get_session_info($sessionId); + $extraSession = new ExtraFieldValue('session'); + $extraField = new ExtraField('session'); + // Get session fields + $fieldList = $extraField->get_all([ + 'variable IN ( ?, ?, ?, ?, ?, ?)' => $fieldsArray, + ]); + // Index session fields + foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; + } + $params = [' item_id = ? ' => $sessionId]; + $sessionFieldValueList = $extraSession->get_all(['where' => $params]); + foreach ($sessionFieldValueList as $sessionFieldValue) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } + } + $adminsArray = UserManager::get_all_administrators(); + + $data['action'] = 'confirm'; + $data['sessionId'] = $sessionId; + $data['currentUserId'] = api_get_user_id(); + $isWesternNameOrder = api_is_western_name_order(); + + foreach ($studentList['students'] as &$student) { + $studentId = intval($student['user_id']); + $data['studentUserId'] = $studentId; + + $fieldValue = new ExtraFieldValue('user'); + $areaField = $fieldValue->get_values_by_handler_and_field_variable($studentId, 'area', true); + + $student['area'] = $areaField['value']; + if (substr($student['area'], 0, 6) == 'MINEDU') { + $student['institution'] = 'Minedu'; + } else { + $student['institution'] = 'Regiones'; + } + $student['userLink'] = api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$studentId; + $data['queueId'] = intval($student['queue_id']); + $data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED; + $data['profile_completed'] = 100; + $student['acceptUrl'] = $plugin->getQueueUrl($data); + $data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED; + $student['rejectUrl'] = $plugin->getQueueUrl($data); + $student['complete_name'] = $isWesternNameOrder ? + $student['firstname'].', '.$student['lastname'] : $student['lastname'].', '.$student['firstname']; + } + $tpl->assign('session', $sessionArray); + $tpl->assign('students', $studentList['students']); +} + +// Assign variables +$tpl->assign('sessionItems', $sessionList); +$tpl->assign('approveAdmin', ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_APPROVED); +$tpl->assign('disapproveAdmin', ADVANCED_SUBSCRIPTION_QUEUE_STATUS_ADMIN_DISAPPROVED); +// Get rendered template +$content = $tpl->fetch('/advanced_subscription/views/admin_view.tpl'); +// Assign into content +$tpl->assign('content', $content); +// Display +$tpl->display_one_col_template(); diff --git a/plugin/advanced_subscription/src/open_session.php b/plugin/advanced_subscription/src/open_session.php new file mode 100644 index 000000000..70a4ceea2 --- /dev/null +++ b/plugin/advanced_subscription/src/open_session.php @@ -0,0 +1,61 @@ + + * + * @package chamilo.plugin.advanced_subscription + */ +require_once __DIR__.'/../config.php'; + +$plugin = AdvancedSubscriptionPlugin::create(); + +if (!isset($_GET['session_id'], $_GET['user_id'], $_GET['profile_completed'])) { + exit; +} + +$sessionInfo = api_get_session_info($_GET['session_id']); + +$tpl = new Template( + $plugin->get_lang('plugin_title'), + false, + false, + false, + false, + false +); +$tpl->assign('session', $sessionInfo); + +if (SessionManager::isUserSubscribedAsStudent( + $_GET['session_id'], + $_GET['user_id'] +)) { + $tpl->assign('is_subscribed', false); + $tpl->assign( + 'errorMessages', + [sprintf( + $plugin->get_lang('YouAreAlreadySubscribedToSessionX'), + $sessionInfo['name'] + )] + ); +} else { + if (!$plugin->isAllowedSubscribeToOpenSession($_GET)) { + $tpl->assign('is_subscribed', false); + $tpl->assign('errorMessages', $plugin->getErrorMessages()); + } else { + SessionManager::subscribeUsersToSession( + $_GET['session_id'], + [$_GET['user_id']], + SESSION_VISIBLE_READ_ONLY, + false + ); + + $tpl->assign('is_subscribed', true); + } +} + +$content = $tpl->fetch('/advanced_subscription/views/open_session.tpl'); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/advanced_subscription/src/render_mail.php b/plugin/advanced_subscription/src/render_mail.php new file mode 100644 index 000000000..da23d9230 --- /dev/null +++ b/plugin/advanced_subscription/src/render_mail.php @@ -0,0 +1,26 @@ +checkHash($data, $hash); +if ($verified) { + // Render mail + $message = MessageManager::get_message_by_id($data['queueId']); + $message = str_replace(['

', '
', '
'], '', $message['content']); + echo $message; +} diff --git a/plugin/advanced_subscription/src/scripts/insert_session_fields.php b/plugin/advanced_subscription/src/scripts/insert_session_fields.php new file mode 100644 index 000000000..913ad09ef --- /dev/null +++ b/plugin/advanced_subscription/src/scripts/insert_session_fields.php @@ -0,0 +1,204 @@ +save([ + 'field_type' => ExtraField::FIELD_TYPE_INTEGER, + 'variable' => 'teaching_hours', + 'display_text' => get_lang('TeachingHours'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$cost = new ExtraField('session'); +$cost->save([ + 'field_type' => ExtraField::FIELD_TYPE_FLOAT, + 'variable' => 'cost', + 'display_text' => get_lang('Cost'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$vacancies = new ExtraField('session'); +$vacancies->save([ + 'field_type' => ExtraField::FIELD_TYPE_INTEGER, + 'variable' => 'vacancies', + 'display_text' => get_lang('Vacancies'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$recommendedNumberOfParticipants = new ExtraField('session'); +$recommendedNumberOfParticipants->save([ + 'field_type' => ExtraField::FIELD_TYPE_INTEGER, + 'variable' => 'recommended_number_of_participants', + 'display_text' => get_lang('RecommendedNumberOfParticipants'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$place = new ExtraField('session'); +$place->save([ + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'variable' => 'place', + 'display_text' => get_lang('Place'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$schedule = new ExtraField('session'); +$schedule->save([ + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'variable' => 'schedule', + 'display_text' => get_lang('Schedule'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$allowVisitors = new ExtraField('session'); +$allowVisitors->save([ + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + 'variable' => 'allow_visitors', + 'display_text' => get_lang('AllowVisitors'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$modeOptions = [ + get_lang('Online'), + get_lang('Presencial'), + get_lang('B-Learning'), +]; + +$mode = new ExtraField('session'); +$mode->save([ + 'field_type' => ExtraField::FIELD_TYPE_SELECT, + 'variable' => 'mode', + 'display_text' => get_lang('Mode'), + 'visible_to_self' => 1, + 'changeable' => 1, + 'field_options' => implode('; ', $modeOptions), +]); + +$isInductionSession = new ExtraField('session'); +$isInductionSession->save([ + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + 'variable' => 'is_induction_session', + 'display_text' => get_lang('IsInductionSession'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$isOpenSession = new ExtraField('session'); +$isOpenSession->save([ + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + 'variable' => 'is_open_session', + 'display_text' => get_lang('IsOpenSession'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$duration = new ExtraField('session'); +$duration->save([ + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'variable' => 'human_text_duration', + 'display_text' => get_lang('DurationInWords'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$showStatusOptions = [ + get_lang('Open'), + get_lang('InProcess'), + get_lang('Closed'), +]; + +$showStatus = new ExtraField('session'); +$showStatus->save([ + 'field_type' => ExtraField::FIELD_TYPE_SELECT, + 'variable' => 'show_status', + 'display_text' => get_lang('ShowStatus'), + 'visible_to_self' => 1, + 'changeable' => 1, + 'field_options' => implode('; ', $showStatusOptions), +]); + +$publicationStartDate = new ExtraField('session'); +$publicationStartDate->save([ + 'field_type' => ExtraField::FIELD_TYPE_DATE, + 'variable' => 'publication_start_date', + 'display_text' => get_lang('PublicationStartDate'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$publicationEndDate = new ExtraField('session'); +$publicationEndDate->save([ + 'field_type' => ExtraField::FIELD_TYPE_DATE, + 'variable' => 'publication_end_date', + 'display_text' => get_lang('PublicationEndDate'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$banner = new ExtraField('session'); +$banner->save([ + 'field_type' => ExtraField::FIELD_TYPE_FILE_IMAGE, + 'variable' => 'banner', + 'display_text' => get_lang('SessionBanner'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$brochure = new ExtraField('session'); +$brochure->save([ + 'field_type' => ExtraField::FIELD_TYPE_FILE, + 'variable' => 'brochure', + 'display_text' => get_lang('Brochure'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$targetOptions = [ + get_lang('Minedu'), + get_lang('Regiones'), +]; + +$target = new ExtraField('session'); +$target->save([ + 'field_type' => ExtraField::FIELD_TYPE_SELECT, + 'variable' => 'target', + 'display_text' => get_lang('TargetAudience'), + 'visible_to_self' => 1, + 'changeable' => 1, + 'field_options' => implode('; ', $targetOptions), +]); + +$shortDescription = new ExtraField('session'); +$shortDescription->save([ + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'variable' => 'short_description', + 'display_text' => get_lang('ShortDescription'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); + +$id = new ExtraField('session'); +$id->save([ + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'variable' => 'code', + 'display_text' => get_lang('Code'), + 'visible_to_self' => 1, + 'changeable' => 1, +]); diff --git a/plugin/advanced_subscription/src/scripts/insert_sessions_categories.php b/plugin/advanced_subscription/src/scripts/insert_sessions_categories.php new file mode 100644 index 000000000..6395be8ae --- /dev/null +++ b/plugin/advanced_subscription/src/scripts/insert_sessions_categories.php @@ -0,0 +1,21 @@ +get_lang('plugin_title')); + +$isAllowToDoRequest = $plugin->isAllowedToDoRequest($data['studentUserId'], $data, true); + +if (!$isAllowToDoRequest) { + $tpl->assign('errorMessages', $plugin->getErrorMessages()); +} + +if ( + !empty($data['sessionId']) && + !empty($data['studentUserId']) && + api_get_plugin_setting('courselegal', 'tool_enable') +) { + $lastMessageId = $plugin->getLastMessageId($data['studentUserId'], $data['sessionId']); + if ($lastMessageId !== false) { + // Render mail + $url = $plugin->getRenderMailUrl(['queueId' => $lastMessageId]); + header('Location: '.$url); + exit; + } + $courses = SessionManager::get_course_list_by_session_id($data['sessionId']); + $course = current($courses); + $data['courseId'] = $course['id']; + $legalEnabled = api_get_plugin_setting('courselegal', 'tool_enable'); + if ($legalEnabled) { + $courseLegal = CourseLegalPlugin::create(); + $termsAndConditions = $courseLegal->getData($data['courseId'], $data['sessionId']); + $termsAndConditions = $termsAndConditions['content']; + $termFiles = $courseLegal->getCurrentFile($data['courseId'], $data['sessionId']); + } else { + $termsAndConditions = $plugin->get('terms_and_conditions'); + $termFiles = ''; + } + + $data['session'] = api_get_session_info($data['sessionId']); + $data['student'] = api_get_user_info($data['studentUserId']); + $data['course'] = api_get_course_info_by_id($data['courseId']); + $data['acceptTermsUrl'] = $plugin->getQueueUrl($data); + $data['rejectTermsUrl'] = $plugin->getTermsUrl($data, ADVANCED_SUBSCRIPTION_TERMS_MODE_REJECT); + // Use Twig with String loader + $termsContent = $plugin->renderTemplateString($termsAndConditions, $data); +} else { + $termsContent = ''; + $termFiles = ''; + $data['acceptTermsUrl'] = '#'; + $data['rejectTermsUrl'] = '#'; +} + +// Assign into content +$tpl->assign('termsRejected', $data['termsRejected']); +$tpl->assign('acceptTermsUrl', $data['acceptTermsUrl']); +$tpl->assign('rejectTermsUrl', $data['rejectTermsUrl']); +$tpl->assign('session', $data['session']); +$tpl->assign('student', $data['student']); +$tpl->assign('sessionId', $data['sessionId']); +$tpl->assign('termsContent', $termsContent); +$tpl->assign('termsFiles', $termFiles); + +$content = $tpl->fetch('/advanced_subscription/views/terms_and_conditions.tpl'); +echo $content; diff --git a/plugin/advanced_subscription/test/mails.php b/plugin/advanced_subscription/test/mails.php new file mode 100644 index 000000000..824c061eb --- /dev/null +++ b/plugin/advanced_subscription/test/mails.php @@ -0,0 +1,134 @@ +get_all([ + 'variable IN ( ?, ?, ?, ?, ?)' => $fieldsArray, +]); +$fields = []; +// Index session fields +foreach ($fieldList as $field) { + $fields[$field['id']] = $field['variable']; +} + +$mergedArray = array_merge([$data['sessionId']], array_keys($fields)); +$sessionFieldValueList = $extraSession->get_all( + ['item_id = ? field_id IN ( ?, ?, ?, ?, ?, ?, ? )' => $mergedArray] +); +foreach ($sessionFieldValueList as $sessionFieldValue) { + // Check if session field value is set in session field list + if (isset($fields[$sessionFieldValue['field_id']])) { + $var = $fields[$sessionFieldValue['field_id']]; + $val = $sessionFieldValue['value']; + // Assign session field value to session + $sessionArray[$var] = $val; + } +} +// Get student data +$studentArray = api_get_user_info($data['studentUserId']); +$studentArray['picture'] = $studentArray['avatar']; + +// Get superior data if exist +$superiorId = UserManager::getFirstStudentBoss($data['studentUserId']); +if (!empty($superiorId)) { + $superiorArray = api_get_user_info($superiorId); +} else { + $superiorArray = api_get_user_info(3); +} +// Get admin data +$adminsArray = UserManager::get_all_administrators(); +$isWesternNameOrder = api_is_western_name_order(); +foreach ($adminsArray as &$admin) { + $admin['complete_name'] = $isWesternNameOrder ? + $admin['firstname'].', '.$admin['lastname'] : $admin['lastname'].', '.$admin['firstname'] + ; +} +unset($admin); +// Set data +$data['action'] = 'confirm'; +$data['student'] = $studentArray; +$data['superior'] = $superiorArray; +$data['admins'] = $adminsArray; +$data['admin'] = current($adminsArray); +$data['session'] = $sessionArray; +$data['signature'] = api_get_setting('Institution'); +$data['admin_view_url'] = api_get_path(WEB_PLUGIN_PATH). + 'advanced_subscription/src/admin_view.php?s='.$data['sessionId']; +$data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_APPROVED; +$data['student']['acceptUrl'] = $plugin->getQueueUrl($data); +$data['newStatus'] = ADVANCED_SUBSCRIPTION_QUEUE_STATUS_BOSS_DISAPPROVED; +$data['student']['rejectUrl'] = $plugin->getQueueUrl($data); +$tpl = new Template($plugin->get_lang('plugin_title')); +$tpl->assign('data', $data); +$tplParams = [ + 'user', + 'student', + 'students', + 'superior', + 'admins', + 'admin', + 'session', + 'signature', + 'admin_view_url', + 'acceptUrl', + 'rejectUrl', +]; +foreach ($tplParams as $tplParam) { + $tpl->assign($tplParam, $data[$tplParam]); +} + +$dir = __DIR__.'/../views/'; +$files = scandir($dir); + +echo '
', '
', print_r($files, 1), '
'; + +foreach ($files as $k => &$file) { + if ( + is_file($dir.$file) && + strpos($file, '.tpl') && + $file != 'admin_view.tpl' + ) { + echo '
', $file, '
'; + echo $tpl->fetch('/advanced_subscription/views/'.$file); + } else { + unset($files[$k]); + } +} +echo '
', '
', print_r($files, 1), '
'; diff --git a/plugin/advanced_subscription/test/terms_to_pdf.php b/plugin/advanced_subscription/test/terms_to_pdf.php new file mode 100644 index 000000000..fe363fbb0 --- /dev/null +++ b/plugin/advanced_subscription/test/terms_to_pdf.php @@ -0,0 +1,47 @@ +getData($data['courseId'], $data['sessionId']); + $termsAndConditions = $termsAndConditions['content']; + $termsAndConditions = $plugin->renderTemplateString($termsAndConditions, $data); + $tpl = new Template($plugin->get_lang('Terms')); + $tpl->assign('session', $data['session']); + $tpl->assign('student', $data['student']); + $tpl->assign('sessionId', $data['sessionId']); + $tpl->assign('termsContent', $termsAndConditions); + $termsAndConditions = $tpl->fetch('/advanced_subscription/views/terms_and_conditions_to_pdf.tpl'); + $pdf = new PDF(); + $filename = 'terms'.sha1(rand(0, 99999)); + $pdf->content_to_pdf($termsAndConditions, null, $filename, null, 'F'); + $fileDir = api_get_path(WEB_ARCHIVE_PATH).$filename.'.pdf'; + echo '
', print_r($fileDir, 1), '
'; + } +} diff --git a/plugin/advanced_subscription/test/ws_session_user.php b/plugin/advanced_subscription/test/ws_session_user.php new file mode 100644 index 000000000..4bd196c20 --- /dev/null +++ b/plugin/advanced_subscription/test/ws_session_user.php @@ -0,0 +1,86 @@ + $wsUrl, + 'uri' => $wsUrl, +]; + +/** + * WS test. + */ +try { + // Init soap client + $client = new SoapClient(null, $options); + // Soap call to WS + $result = $client->__soapCall('HookAdvancedSubscription..WSSessionGetDetailsByUser', [$params]); + if (is_object($result) && isset($result->action_url)) { + echo '
'; + echo Display::url("message".$result->message, $result->action_url); + } +} catch (\Exception $e) { + var_dump($e); +} diff --git a/plugin/advanced_subscription/uninstall.php b/plugin/advanced_subscription/uninstall.php new file mode 100644 index 000000000..0de327718 --- /dev/null +++ b/plugin/advanced_subscription/uninstall.php @@ -0,0 +1,18 @@ +uninstall(); diff --git a/plugin/advanced_subscription/views/admin_accepted_notice_admin.tpl b/plugin/advanced_subscription/views/admin_accepted_notice_admin.tpl new file mode 100644 index 000000000..6bd49c602 --- /dev/null +++ b/plugin/advanced_subscription/views/admin_accepted_notice_admin.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminAcceptToAdmin"| get_plugin_lang('AdvancedSubscriptionPlugin') }} 
   
   
 

{{ "MailDear" | get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ admin.complete_name }}

+

{{ "MailContentAdminAcceptToAdmin" | get_plugin_lang('AdvancedSubscriptionPlugin') | format(student.complete_name_with_username, session.name) }}

+

{{ "MailThankYou" | get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_accepted_notice_student.tpl b/plugin/advanced_subscription/views/admin_accepted_notice_student.tpl new file mode 100644 index 000000000..d4e4ae8cc --- /dev/null +++ b/plugin/advanced_subscription/views/admin_accepted_notice_student.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle" | get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminAcceptToStudent" | get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ student.complete_name }}

+

{{ "MailContentAdminAcceptToStudent" | get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name, session.date_start) }}

+

{{ "MailThankYou" | get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_accepted_notice_superior.tpl b/plugin/advanced_subscription/views/admin_accepted_notice_superior.tpl new file mode 100644 index 000000000..e072640b3 --- /dev/null +++ b/plugin/advanced_subscription/views/admin_accepted_notice_superior.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminAcceptToSuperior"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(student.complete_name, session.name) }} 
   
   
 

{{ "MailDear"| get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ superior.complete_name }}

+

{{ "MailContentAdminAcceptToSuperior"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(student.complete_name, session.name, session.date_start) }}

+

{{ "MailThankYou"| get_plugin_lang('AdvancedSubscriptionPlugin') }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_rejected_notice_admin.tpl b/plugin/advanced_subscription/views/admin_rejected_notice_admin.tpl new file mode 100644 index 000000000..3610ffd81 --- /dev/null +++ b/plugin/advanced_subscription/views/admin_rejected_notice_admin.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminRejectToAdmin" | get_plugin_lang('AdvancedSubscriptionPlugin') }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ admin.complete_name }}

+

{{ "MailContentAdminRejectToAdmin"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(student.complete_name_with_username, session.name) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_rejected_notice_student.tpl b/plugin/advanced_subscription/views/admin_rejected_notice_student.tpl new file mode 100644 index 000000000..35e1292f9 --- /dev/null +++ b/plugin/advanced_subscription/views/admin_rejected_notice_student.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminRejectToStudent"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentAdminRejectToStudent"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name, session.date_start) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_rejected_notice_superior.tpl b/plugin/advanced_subscription/views/admin_rejected_notice_superior.tpl new file mode 100644 index 000000000..926d675cb --- /dev/null +++ b/plugin/advanced_subscription/views/admin_rejected_notice_superior.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleAdminRejectToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name, session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentAdminRejectToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name, session.name) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/admin_view.tpl b/plugin/advanced_subscription/views/admin_view.tpl new file mode 100644 index 000000000..532b0c7c3 --- /dev/null +++ b/plugin/advanced_subscription/views/admin_view.tpl @@ -0,0 +1,167 @@ + + + + diff --git a/plugin/advanced_subscription/views/css/style.css b/plugin/advanced_subscription/views/css/style.css new file mode 100644 index 000000000..317fca713 --- /dev/null +++ b/plugin/advanced_subscription/views/css/style.css @@ -0,0 +1,84 @@ +.text-title-select{ + display: inline-block; +} +#session-select{ + display: inline-block; +} +.title-name-session{ + display: block; + padding-top: 10px; + padding-bottom: 10px; + font-weight: normal; + margin-top: 5px; + margin-bottom: 5px; +} +.badge-dis{ + background-color: #008080; + font-size: 20px; +} +.badge-recom{ + background-color:#88aa00 ; + font-size: 20px; +} +.badge-info { + font-size: 20px; +} +.separate-badge{ + margin-bottom: 20px; + margin-top: 20px; +} +.date, .mode{ + display: inline-block; +} +.img-circle{ + border-radius: 500px; + -moz-border-radius: 500px; + -webkit-border-radius: 500px; +} +#student_table.table td{ + vertical-align: middle; + text-align: center; +} +#student_table.table td.name{ + color: #084B8A; + text-align: left; + +} +#student_table.table th{ + font-size: 14px; + vertical-align: middle; + text-align: center; +} + +#modalMail .modal-body { + height: 360px; + overflow: visible; +} + +#iframeAdvsub { + width: 100%; + height: 100%; +} + +.legal-terms-popup{ + margin-top: 5%; + margin-left: 5%; + margin-right: 5%; +} + +.legal-terms{ + width: 90%; + height: 50%; +} + +.legal-terms-buttons { + text-align: right; +} + +.legal-terms-title { + text-align: center; +} + +.advanced-subscription-decision-column { + width: 200px; +} diff --git a/plugin/advanced_subscription/views/img/aprobar.png b/plugin/advanced_subscription/views/img/aprobar.png new file mode 100644 index 0000000000000000000000000000000000000000..90250ca75277a5c1d5db591d3a1325e88f262d3b GIT binary patch literal 1482 zcmV;*1vUDKP)X1^@s6v1B>G00004b3#c}2nYxW zd>|G0X8>q!`G~g8Jvi+1S#bZSBoa~1G`~kh*&Fl(4${`KoAQ#) z6!~97mK?;D5MAB-X*$0HWqdT5-ynN<_`%QdxR;?QVNNx^NpIi~PWu8D%-e;@ma{rhiGWrMqC-8ymSkG-+KBkA0ZO$!|&U`$5ec2*Y@dt|eG4rNraI=yew6RJ@DDT#QaMU^eArwUn@E{$3ne4mXZ{^ z;ozUt*01I8PfLg^AwVD}``Wvcs*ZEA;T4Y5E~CBk3rhT(065#coyNaDk1b)25Q0|3a9BRQ8x!p#7Lue49*5vM!@#v(n$<3S`T3!}k{ZAJwP=k1>S!Bxh` zqsf}DCp_BDz|aW|)JR3sj?Q*1MV9OU6y`t2y!&<}1-jblQ(-X|0}zc~o^m~|giuul zm=GO6(p@_cW3ho7e2fMUl9V+OHx^*_taq?lO1N<8YrZ>NNz?fqx4XUngr*&x=bd=k z7#OOjZE`-FOg`pTypPRV&J0Ug(pHik6X-vN!H|7JTvb)*AFLzW zwUn}=SBWbjN()~CAQ);Q5)CA6O~wMeo+l_S*a$!@KFr|o@06Erz8VdqF&DS9TFb59 z6wQPdMTy*;Cjq$F{S7^VgOe))vr1mWAiMG9t|1m10>Es{XHnJONgI`OEjtN~woWND zKtt2pR9EfA>sf)KKJy$Zi?1AgBI z!lUh4ZT*%IzCDnyY7t1MGvmmb2TUAv*FSREW>g|ec4F}%+>UDIR(`;6uz}M}Z(%hT z5e%IpJlYCO99~(r*{JF`14H#GIYiNb$F&r*serLaH~oXZOdg~YQ965urilRW4We9@6Ehgpw;y6aD)q>+V2!deY`(;_i`T05a_V!qZ z1^^IPCc)?DXV|ul@p$kZs#v zN~Ft(6hNobK^(`FmQ$i$1*8C2Ed9@NT^C7`kol1T7>~zX006+-+Z&k|8GtyBNk5st zi82q60pK-x0Hk^5TLReFpxeQd0T_)&R8|WmNfMbC8Gw8~Pi3`GnOp(+d>*36r@fF# z0Ki8n%R`y`3IJeMv9TQI+W}BjHM?H{9sqWCcS-%eT@J`JfK^&w%PN=4k@}GWs8lMH zc3j3FEr!A{q_Uf`9`Aw-}mVPnjvNY z;y6a7Qc0ZyEXzU^MTC(}ipfPu$8qrV^h9=PLXsq$ot>rHRn8MBfKI1_Mx%jVuSaD$ zAx+azEEb{bI#~lYHGnvd(QG!+Y&N-^3jiO5mT8(WO*3_$U}XUPejhI{FK}I#>-CsF zB?!Xrf@{@w+0paqswz%TPrnRe zA%NjRHK$<&E1sE1W>Ehxc#Q{P!vUT|Hv@~ zpxf=jaUAAB@m8zVLbuzUdVLDO>+36*fYzZ|bDILdY9F5k&6?W;fFwz{G$GcdAPAz00000NkvXXu0mjfyDsV@ literal 0 HcmV?d00001 diff --git a/plugin/advanced_subscription/views/img/desaprobar.png b/plugin/advanced_subscription/views/img/desaprobar.png new file mode 100644 index 0000000000000000000000000000000000000000..1d73cd739ee151bbb18b6d7628674accc8e163b8 GIT binary patch literal 1901 zcmV-z2a@=SP)X1^@s6v1B>G00004b3#c}2nYxW zd{Q3{hh;`vWJ;$@87nvrw$d_CI|5q75$KFn>j-p2+Xib8fyl1_ z0g?+jlHBgy?Y{kE(}NU`hCvbs^ZDmy_kG^?`M%$M?%BPY6&LDe3_+@;O0WP3Gr$)~ zyHKph5i9g=lOOGGMK>s6C3sXwh3u~60j?uTus|}~95M6$Xs%BlIf`3SR(` z|26kODKNWG7tW)SW?yK#Zs-%IppTn`bK)?`zisWCoL3su%E#kh^mNaYj!k<1hgh$@ zh|_%RdTP6FXeA>tYVIT*7zBM}bgx@SYWEIOAH0p~>OiloA@HMLA_ot-EKYd+AVijI z612~`wn88Rq3rW6i9=ppw-Y}9{}0NxrqmGrUg+utSFGquv9_&4bzMLVE+F{$ZxDkE z@XlRC;>G141fJVx}R_v4v17cnRoWhO}1zlZhbwWzL+%$`JW5dMWr(Z|=K(kZMh z&y(8wS7cEc{zXqCBT?66J59P_FXqduQSlg_sdF%<&c;6U5l&Mh#y9W7eEC`IL;JG& zchxsT)pm~L-~R%DHhe4q#*}a4om+?MIFGq`4fe^;Fsi5Fo%J9hCqm%izClJ9QAX*-8jJ|5q^C9V-(A2Jd}yJ-ws*=STpJHq3|bI&}q zVHKbo$Ve0!iQ-?p0&PSk!XLsjbq?NH4}zwVeCrid>isuyP8=q!d(g^8qmQd4wPz<7UI3hvjif)`OLFHQ5uq@u zqn*xW3lX{bXeA>Nx`CWL6Ro7YM?RXAOA3umn zS_B?{1_0~zKakq>2B|$e2|l?FeZpi!ZhmG?x?wMg7oI}+{Df9+1;c|jY9i@>zDw7y z79$Iakws;QoWbbhYA@Gywh43dbExwzs0(dhywM`B&^0_BLsApz2cd;a2|Hk zXV@nWqm_)rJEsosoI32I2QW9T1i-&=DOyF9yI#_ACuZxJO5vP5f{a8F;k+J0srucJ zvOr2?ZUF$!>7(xFxs#bVSx|iWvStD&){M+ALgwZn3ySbR^c1pqc=zJny0o6UoY)!c zgY{Wl>d93bEka?8+PeU-8$X6r5@nePpP$6K<=Bl4JtsIeUBCV@#-thOqb8zN)}WP* z#&gH_QRiFGDyp!LeM;hmpCKbd2rOBB#r$%}PykdsO9eR&D$$7ugb=|TSF<;e8CD(V zFW-~Lm$|X4cpIL3=Oc@Uldj)|`O+%%${Kw0pXil8+32;=o_l|Y&<*slw;*!!QSGf* zul)`H`;!mQtG7Or4F>+MFp2r?na9Ow+}6 zbMw(FYcOi>c5l*2i}b;L=--%jQCCg`t>Wh1+LV>v8-3fXOmh3ve__7zJWlHw0L)Em z@Q1=^@2RC{aI#;=u(L>88UH-Kt8i{1Ed zl$k&lmZA+E0lok#c9!(PdPt|-9Dxw}_*+r&HtZw&kwZrUU?19tQ9TXc{3mc)PLcTS zGGt*XPV;e`Q%Bsn9v^a8g&TkPBlp*9MvRVLQH3lh#%XWGKC}FSZ*LsrY;X`@aTNXhUviRva@1lAzFDg{p4~iSX$&;j;mX!(pyD+u6^slX!a(ON z;f!NoxWuB*z>wJ_&d^|Tkek6_)&o`s1>X-$3=`A{DtlXZFaJBxLlMUg!*sr7f4P{= V_r;&f&w(~Ec)I$ztaD0e0st0QQ1$=- literal 0 HcmV?d00001 diff --git a/plugin/advanced_subscription/views/img/header.png b/plugin/advanced_subscription/views/img/header.png new file mode 100644 index 0000000000000000000000000000000000000000..45580646798ece8b4c4ef716cf5bb427bf16ba2c GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0y~yVA=y@i*T?3$^5K$FM$+Gv6E*A2N2Y7q;vrJoCO|{ z#X$AfL734=V|E2lP_o1|q9iy!t)x7$D3zhSyj(9cFS|H7u^?41zbJk7I~ysWq7F|N z$B>FSZ*MqqG8hW596a6GThA^ns(2wpb4I#xcG@#$hK3dYzXDb6@ch8UFhQ+?k-aSRNXSo9efGMmI18cYsyGdRq8z{;TDOPsRYw|k#|P000{Z1^@s6i6JCt00004b3#c}2nYxW zdi9C5@j#V(6^-5QN%N z`3Mw3Yhg!YsEvs|i5)f=L%~}?2y!ggjY4)_?g}S)nmx16f6ttmoe>do!X#efJ-*-q z2Y83c()$siJb)cULI>C^!AgL)8Nk2V&G`%(v(6X{2B(-!4)_~acped_UCChvuuutr zDZEN0lMpOd0@`@m2XZ+k!ND zfaPikR`I#QxJ&z3O+v7Uy`JXxwTHz!5Ujhs>O2IX)dkkuqh!VHMc^R;t*)>#fi1l7 jI5DAF=7*0|MXSRgcyqV(DCY@~pSW_Y?d zhE&XXd(DxTK|$bv!GZnHommwlqgbo+&Y#g?VCb;g&& + + + + + +{% if not is_subscribed %} + + + +{% else %} + + + +{% endif %} diff --git a/plugin/advanced_subscription/views/reminder_notice_admin.tpl b/plugin/advanced_subscription/views/reminder_notice_admin.tpl new file mode 100644 index 000000000..a76474063 --- /dev/null +++ b/plugin/advanced_subscription/views/reminder_notice_admin.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleReminderAdmin" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name)}} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ admin.complete_name }}

+

{{ "MailContentReminderAdmin" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, admin_view_url)}}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/reminder_notice_student.tpl b/plugin/advanced_subscription/views/reminder_notice_student.tpl new file mode 100644 index 000000000..7ff1f5e10 --- /dev/null +++ b/plugin/advanced_subscription/views/reminder_notice_student.tpl @@ -0,0 +1,76 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleReminderStudent"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentReminderStudent"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, session.date_start) }}

+

{{ "MailContentReminderStudentSecond"| get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ "MailThankYouCollaboration"| get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/reminder_notice_superior.tpl b/plugin/advanced_subscription/views/reminder_notice_superior.tpl new file mode 100644 index 000000000..cc78893d2 --- /dev/null +++ b/plugin/advanced_subscription/views/reminder_notice_superior.tpl @@ -0,0 +1,86 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleReminderSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentReminderSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, session.date_start, session.description) }}

+

{{ "MailContentReminderSuperiorSecond" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+ + {% for student in students %} + + + + + + + {% endfor %} +

{{ student.complete_name }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/reminder_notice_superior_max.tpl b/plugin/advanced_subscription/views/reminder_notice_superior_max.tpl new file mode 100644 index 000000000..cdc1c6af3 --- /dev/null +++ b/plugin/advanced_subscription/views/reminder_notice_superior_max.tpl @@ -0,0 +1,87 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleReminderMaxSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") }} 
   
   
  +

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentReminderMaxSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, session.date_start, session.description) }}

+

{{ "MailContentReminderMaxSuperiorSecond" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.recommended_number_of_participants) }}

+ + {% for student in students %} + + + + + + + {% endfor %} +

{{ student.complete_name }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/student_no_superior_notice_admin.tpl b/plugin/advanced_subscription/views/student_no_superior_notice_admin.tpl new file mode 100644 index 000000000..0662c13cd --- /dev/null +++ b/plugin/advanced_subscription/views/student_no_superior_notice_admin.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleStudentRequestNoSuperiorToAdmin"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name, session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ admin.complete_name }}

+

{{ "MailContentStudentRequestNoSuperiorToAdmin"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name_with_username, session.name, admin_view_url) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/student_no_superior_notice_student.tpl b/plugin/advanced_subscription/views/student_no_superior_notice_student.tpl new file mode 100644 index 000000000..117e54ffd --- /dev/null +++ b/plugin/advanced_subscription/views/student_no_superior_notice_student.tpl @@ -0,0 +1,76 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleStudentRequestNoSuperiorToStudent"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentStudentRequestNoSuperiorToStudent"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, session.date_start) }}

+

{{ "MailContentStudentRequestNoSuperiorToStudentSecond"| get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/student_notice_student.tpl b/plugin/advanced_subscription/views/student_notice_student.tpl new file mode 100644 index 000000000..b3a473449 --- /dev/null +++ b/plugin/advanced_subscription/views/student_notice_student.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleStudentRequestToStudent"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentStudentRequestToStudent"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, session.date_start) }}

+

{{ "MailContentStudentRequestToStudentSecond" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/student_notice_superior.tpl b/plugin/advanced_subscription/views/student_notice_superior.tpl new file mode 100644 index 000000000..65c9ab98d --- /dev/null +++ b/plugin/advanced_subscription/views/student_notice_superior.tpl @@ -0,0 +1,84 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleStudentRequestToSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentStudentRequestToSuperior" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name, session.name, session.date_start, session.description) }}

+

{{ "MailContentStudentRequestToSuperiorSecond" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+ + + + + + + +

{{ student.complete_name }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/superior_accepted_notice_admin.tpl b/plugin/advanced_subscription/views/superior_accepted_notice_admin.tpl new file mode 100644 index 000000000..2a15922c6 --- /dev/null +++ b/plugin/advanced_subscription/views/superior_accepted_notice_admin.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleSuperiorAcceptToAdmin"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name, session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ admin.complete_name }}

+

{{ "MailContentSuperiorAcceptToAdmin"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name_with_username, session.name, admin_view_url) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/superior_accepted_notice_student.tpl b/plugin/advanced_subscription/views/superior_accepted_notice_student.tpl new file mode 100644 index 000000000..b2d39fe69 --- /dev/null +++ b/plugin/advanced_subscription/views/superior_accepted_notice_student.tpl @@ -0,0 +1,76 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleSuperiorAcceptToStudent" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name) }} 
   
   
  +

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentSuperiorAcceptToStudent" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name ) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/superior_accepted_notice_superior.tpl b/plugin/advanced_subscription/views/superior_accepted_notice_superior.tpl new file mode 100644 index 000000000..938d3057f --- /dev/null +++ b/plugin/advanced_subscription/views/superior_accepted_notice_superior.tpl @@ -0,0 +1,76 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleSuperiorAcceptToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentSuperiorAcceptToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, student.complete_name) }}

+

{{ "MailContentSuperiorAcceptToSuperiorSecond"| get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ "MailThankYouCollaboration" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/superior_rejected_notice_student.tpl b/plugin/advanced_subscription/views/superior_rejected_notice_student.tpl new file mode 100644 index 000000000..4e16e121c --- /dev/null +++ b/plugin/advanced_subscription/views/superior_rejected_notice_student.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleSuperiorRejectToStudent" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ student.complete_name }}

+

{{ "MailContentSuperiorRejectToStudent" | get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name) }}

+

{{ "MailThankYou" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/superior_rejected_notice_superior.tpl b/plugin/advanced_subscription/views/superior_rejected_notice_superior.tpl new file mode 100644 index 000000000..bf8b06ac5 --- /dev/null +++ b/plugin/advanced_subscription/views/superior_rejected_notice_superior.tpl @@ -0,0 +1,75 @@ + + + + + {{ "MailTitle"| get_plugin_lang('AdvancedSubscriptionPlugin') | format(session.name) }} + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Ministerio de Educación 
   
   
 {{ "MailTitleSuperiorRejectToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(student.complete_name) }} 
   
   
 

{{ "MailDear" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ superior.complete_name }}

+

{{ "MailContentSuperiorRejectToSuperior"| get_plugin_lang("AdvancedSubscriptionPlugin") | format(session.name, student.complete_name) }}

+

{{ "MailThankYouCollaboration" | get_plugin_lang("AdvancedSubscriptionPlugin") }}

+

{{ signature }}

 
   
 
+ + diff --git a/plugin/advanced_subscription/views/terms_and_conditions.tpl b/plugin/advanced_subscription/views/terms_and_conditions.tpl new file mode 100644 index 000000000..540fbd367 --- /dev/null +++ b/plugin/advanced_subscription/views/terms_and_conditions.tpl @@ -0,0 +1,61 @@ +{# start copy from head.tpl #} + + + + +{{ prefetch }} +{{ favico }} +{{ browser_specific_head }} + + + +{# Use the latest engine in ie8/ie9 or use google chrome engine if available #} +{# Improve usability in portal devices #} + +{{ title_string }} +{{ css_static_file_to_string }} +{{ js_file_to_string }} +{{ css_custom_file_to_string }} +{{ css_style_print }} +{# end copy from head.tpl #} + +{% if termsRejected == 1 %} + +{% endif %} + +{% if errorMessages is defined %} + +{% endif %} + + + + + + \ No newline at end of file diff --git a/plugin/advanced_subscription/views/terms_and_conditions_to_pdf.tpl b/plugin/advanced_subscription/views/terms_and_conditions_to_pdf.tpl new file mode 100644 index 000000000..dcbbc810e --- /dev/null +++ b/plugin/advanced_subscription/views/terms_and_conditions_to_pdf.tpl @@ -0,0 +1,23 @@ +{# start copy from head.tpl #} + + + + +{{ prefetch }} +{{ favico }} +{{ browser_specific_head }} + + + + +{# Use the latest engine in ie8/ie9 or use google chrome engine if available #} +{# Improve usability in portal devices #} + +{{ title_string }} +{{ css_file_to_string }} +{{ css_style_print }} +{{ js_file_to_string }} +{# end copy from head.tpl #} + \ No newline at end of file diff --git a/plugin/ai_helper/AiHelperPlugin.php b/plugin/ai_helper/AiHelperPlugin.php new file mode 100644 index 000000000..868d56269 --- /dev/null +++ b/plugin/ai_helper/AiHelperPlugin.php @@ -0,0 +1,421 @@ + 'html', + 'tool_enable' => 'boolean', + 'api_name' => [ + 'type' => 'select', + 'options' => $this->getApiList(), + ], + 'api_key' => 'text', + 'organization_id' => 'text', + 'tool_lp_enable' => 'boolean', + 'tool_quiz_enable' => 'boolean', + 'tokens_limit' => 'text', + ]; + + parent::__construct($version, $author, $settings); + } + + /** + * Get the list of APIs available. + * + * @return array + */ + public function getApiList() + { + $list = [ + self::OPENAI_API => 'OpenAI', + self::DEEPSEEK_API => 'DeepSeek', + ]; + + return $list; + } + + /** + * Get the completion text from the selected API. + * + * @return string|array + */ + public function getCompletionText(string $prompt, string $toolName) + { + if (!$this->validateUserTokensLimit(api_get_user_id())) { + return [ + 'error' => true, + 'message' => $this->get_lang('ErrorTokensLimit'), + ]; + } + + $apiName = $this->get('api_name'); + + switch ($apiName) { + case self::OPENAI_API: + return $this->openAiGetCompletionText($prompt, $toolName); + case self::DEEPSEEK_API: + return $this->deepSeekGetCompletionText($prompt, $toolName); + default: + return [ + 'error' => true, + 'message' => 'API not supported.', + ]; + } + } + + /** + * Get completion text from OpenAI. + */ + public function openAiGetCompletionText(string $prompt, string $toolName) + { + try { + require_once __DIR__.'/src/openai/OpenAi.php'; + + $apiKey = $this->get('api_key'); + $organizationId = $this->get('organization_id'); + + $ai = new OpenAi($apiKey, $organizationId); + + $params = [ + 'model' => 'gpt-3.5-turbo-instruct', + 'prompt' => $prompt, + 'temperature' => 0.2, + 'max_tokens' => 2000, + 'frequency_penalty' => 0, + 'presence_penalty' => 0.6, + 'top_p' => 1.0, + ]; + + $complete = $ai->completion($params); + $result = json_decode($complete, true); + + if (isset($result['error'])) { + $errorMessage = $result['error']['message'] ?? 'Unknown error'; + error_log("OpenAI Error: $errorMessage"); + + return [ + 'error' => true, + 'message' => $errorMessage, + ]; + } + + $resultText = $result['choices'][0]['text'] ?? ''; + + if (!empty($resultText)) { + $this->saveRequest([ + 'user_id' => api_get_user_id(), + 'tool_name' => $toolName, + 'prompt' => $prompt, + 'prompt_tokens' => (int) ($result['usage']['prompt_tokens'] ?? 0), + 'completion_tokens' => (int) ($result['usage']['completion_tokens'] ?? 0), + 'total_tokens' => (int) ($result['usage']['total_tokens'] ?? 0), + ]); + } + + return $resultText ?: 'No response generated.'; + } catch (Exception $e) { + return [ + 'error' => true, + 'message' => 'An error occurred while connecting to OpenAI: '.$e->getMessage(), + ]; + } + } + + /** + * Get completion text from DeepSeek. + */ + public function deepSeekGetCompletionText(string $prompt, string $toolName) + { + $apiKey = $this->get('api_key'); + + $url = 'https://api.deepseek.com/chat/completions'; + + $payload = [ + 'model' => 'deepseek-chat', + 'messages' => [ + [ + 'role' => 'system', + 'content' => ($toolName === 'quiz') + ? 'You are a helpful assistant that generates Aiken format questions.' + : 'You are a helpful assistant that generates learning path contents.', + ], + [ + 'role' => 'user', + 'content' => $prompt, + ], + ], + 'stream' => false, + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + "Authorization: Bearer $apiKey", + ]); + + $response = curl_exec($ch); + + if ($response === false) { + error_log('cURL error: '.curl_error($ch)); + curl_close($ch); + + return ['error' => true, 'message' => 'Request to AI provider failed.']; + } + + curl_close($ch); + + $result = json_decode($response, true); + + if (isset($result['error'])) { + return [ + 'error' => true, + 'message' => $result['error']['message'] ?? 'Unknown error', + ]; + } + + $resultText = $result['choices'][0]['message']['content'] ?? ''; + $this->saveRequest([ + 'user_id' => api_get_user_id(), + 'tool_name' => $toolName, + 'prompt' => $prompt, + 'prompt_tokens' => 0, + 'completion_tokens' => 0, + 'total_tokens' => 0, + ]); + + return $resultText; + } + + /** + * Generate questions based on the selected AI provider. + * + * @param int $nQ Number of questions + * @param string $lang Language for the questions + * @param string $topic Topic of the questions + * @param string $questionType Type of questions (e.g., 'multiple_choice') + * + * @throws Exception If an error occurs + * + * @return string Questions generated in Aiken format + */ + public function generateQuestions(int $nQ, string $lang, string $topic, string $questionType = 'multiple_choice'): string + { + $apiName = $this->get('api_name'); + + switch ($apiName) { + case self::OPENAI_API: + return $this->generateOpenAiQuestions($nQ, $lang, $topic, $questionType); + case self::DEEPSEEK_API: + return $this->generateDeepSeekQuestions($nQ, $lang, $topic, $questionType); + default: + throw new Exception("Unsupported API provider: $apiName"); + } + } + + /** + * Validates tokens limit of a user per current month. + */ + public function validateUserTokensLimit(int $userId): bool + { + $em = Database::getManager(); + $repo = $em->getRepository('ChamiloPluginBundle:AiHelper\Requests'); + + $startDate = api_get_utc_datetime( + null, + false, + true) + ->modify('first day of this month')->setTime(00, 00, 00) + ; + $endDate = api_get_utc_datetime( + null, + false, + true) + ->modify('last day of this month')->setTime(23, 59, 59) + ; + + $qb = $repo->createQueryBuilder('e') + ->select('sum(e.totalTokens) as total') + ->andWhere('e.requestedAt BETWEEN :dateMin AND :dateMax') + ->andWhere('e.userId = :user') + ->setMaxResults(1) + ->setParameters( + [ + 'dateMin' => $startDate->format('Y-m-d h:i:s'), + 'dateMax' => $endDate->format('Y-m-d h:i:s'), + 'user' => $userId, + ] + ); + $result = $qb->getQuery()->getOneOrNullResult(); + $totalTokens = !empty($result) ? (int) $result['total'] : 0; + + $valid = true; + $tokensLimit = $this->get('tokens_limit'); + if (!empty($tokensLimit)) { + $valid = ($totalTokens <= (int) $tokensLimit); + } + + return $valid; + } + + /** + * Get the plugin directory name. + */ + public function get_name(): string + { + return 'ai_helper'; + } + + /** + * Get the class instance. + * + * @staticvar AiHelperPlugin $result + */ + public static function create(): AiHelperPlugin + { + static $result = null; + + return $result ?: $result = new self(); + } + + /** + * Save user information of openai request. + * + * @return int + */ + public function saveRequest(array $values) + { + $em = Database::getManager(); + + $objRequest = new Requests(); + $objRequest + ->setUserId($values['user_id']) + ->setToolName($values['tool_name']) + ->setRequestedAt(new DateTime()) + ->setRequestText($values['prompt']) + ->setPromptTokens($values['prompt_tokens']) + ->setCompletionTokens($values['completion_tokens']) + ->setTotalTokens($values['total_tokens']) + ; + $em->persist($objRequest); + $em->flush(); + + return $objRequest->getId(); + } + + /** + * Install the plugin. Set the database up. + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_REQUESTS])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Requests::class), + ] + ); + } + + /** + * Unistall plugin. Clear the database. + */ + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_REQUESTS])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Requests::class), + ] + ); + } + + /** + * Generate questions using OpenAI. + */ + private function generateOpenAiQuestions(int $nQ, string $lang, string $topic, string $questionType): string + { + $prompt = sprintf( + 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', + $nQ, + $questionType, + $lang, + $topic + ); + + $result = $this->openAiGetCompletionText($prompt, 'quiz'); + if (isset($result['error']) && true === $result['error']) { + throw new Exception($result['message']); + } + + return $result; + } + + /** + * Generate questions using DeepSeek. + */ + private function generateDeepSeekQuestions(int $nQ, string $lang, string $topic, string $questionType): string + { + $apiKey = $this->get('api_key'); + $prompt = sprintf( + 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', + $nQ, + $questionType, + $lang, + $topic + ); + $payload = [ + 'model' => 'deepseek-chat', + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'You are a helpful assistant that generates Aiken format questions.', + ], + [ + 'role' => 'user', + 'content' => $prompt, + ], + ], + 'stream' => false, + ]; + + $deepSeek = new DeepSeek($apiKey); + $response = $deepSeek->generateQuestions($payload); + + return $response; + } +} diff --git a/plugin/ai_helper/Entity/Requests.php b/plugin/ai_helper/Entity/Requests.php new file mode 100644 index 000000000..85931cda6 --- /dev/null +++ b/plugin/ai_helper/Entity/Requests.php @@ -0,0 +1,171 @@ +userId; + } + + public function setUserId(int $userId): Requests + { + $this->userId = $userId; + + return $this; + } + + public function getId(): int + { + return $this->id; + } + + public function setId(int $id): Requests + { + $this->id = $id; + + return $this; + } + + public function getRequestedAt(): \DateTime + { + return $this->requestedAt; + } + + public function setRequestedAt(\DateTime $requestedAt): Requests + { + $this->requestedAt = $requestedAt; + + return $this; + } + + public function getRequestText(): string + { + return $this->requestText; + } + + public function setRequestText(string $requestText): Requests + { + $this->requestText = $requestText; + + return $this; + } + + public function getPromptTokens(): int + { + return $this->promptTokens; + } + + public function setPromptTokens(int $promptTokens): Requests + { + $this->promptTokens = $promptTokens; + + return $this; + } + + public function getCompletionTokens(): int + { + return $this->completionTokens; + } + + public function setCompletionTokens(int $completionTokens): Requests + { + $this->completionTokens = $completionTokens; + + return $this; + } + + public function getTotalTokens(): int + { + return $this->totalTokens; + } + + public function setTotalTokens(int $totalTokens): Requests + { + $this->totalTokens = $totalTokens; + + return $this; + } + + public function getToolName(): string + { + return $this->toolName; + } + + public function setToolName(string $toolName): Requests + { + $this->toolName = $toolName; + + return $this; + } +} diff --git a/plugin/ai_helper/README.md b/plugin/ai_helper/README.md new file mode 100644 index 000000000..fb245d8b8 --- /dev/null +++ b/plugin/ai_helper/README.md @@ -0,0 +1,68 @@ +AI Helper Plugin +====== + +Version 1.2 + +> This plugin is designed to integrate AI functionality into Chamilo, providing tools for generating educational content, such as quizzes or learning paths, using AI providers like OpenAI or DeepSeek. + +--- + +### Overview + +The AI Helper plugin integrates into parts of the Chamilo platform that are most useful to teachers/trainers or learners. It allows pre-generating content, letting teachers/trainers review it before publishing. + +Currently, this plugin is integrated into: + +- **Exercises:** In the Aiken import form, with options to generate questions using OpenAI or DeepSeek. +- **Learnpaths:** Option to create structured learning paths with OpenAI or DeepSeek. + +--- + +### Supported AI Providers + +#### OpenAI/ChatGPT +The plugin, created in early 2023, supports OpenAI's ChatGPT API. +- **Setup:** +1. Create an account at [OpenAI](https://platform.openai.com/signup) (or login if you already have one). +2. Generate a secret key at [API Keys](https://platform.openai.com/account/api-keys). +3. Click "Create new secret key", copy the key, and paste it into the "API key" field in the plugin configuration. + +#### DeepSeek +DeepSeek is an alternative Open Source AI provider. +- **Setup:** +1. Create an account at [DeepSeek](https://www.deepseek.com/) (or login if you already have one). +2. Generate an API key at [API Keys](https://platform.deepseek.com/api_keys). +3. Click "Create new API key", copy the key, and paste it into the "API key" field in the plugin configuration. + +--- + +### Features + +- Generate quizzes in the Aiken format using AI. +- Create structured learning paths with AI assistance. +- Support for multiple AI providers, enabling easy switching between OpenAI and DeepSeek. +- Tracks API requests for monitoring usage and limits. + +--- + +### Database Requirements + +No additional database changes are required for v1.2. +The existing table `plugin_ai_helper_requests` is sufficient for tracking requests from both OpenAI and DeepSeek. + +If you're updating from **v1.0**, ensure the following table exists: + +```sql +CREATE TABLE plugin_ai_helper_requests ( + id int(11) NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL, + tool_name varchar(255) COLLATE utf8_unicode_ci NOT NULL, + requested_at datetime DEFAULT NULL, + request_text varchar(255) COLLATE utf8_unicode_ci NOT NULL, + prompt_tokens int(11) NOT NULL, + completion_tokens int(11) NOT NULL, + total_tokens int(11) NOT NULL, + PRIMARY KEY (id) +) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +``` +If you got this update through Git, you will also need to run `composer install` to update the autoload mechanism. diff --git a/plugin/ai_helper/install.php b/plugin/ai_helper/install.php new file mode 100644 index 000000000..ec2627011 --- /dev/null +++ b/plugin/ai_helper/install.php @@ -0,0 +1,16 @@ +install(); diff --git a/plugin/ai_helper/lang/english.php b/plugin/ai_helper/lang/english.php new file mode 100644 index 000000000..36ede273f --- /dev/null +++ b/plugin/ai_helper/lang/english.php @@ -0,0 +1,20 @@ +get_info(); diff --git a/plugin/ai_helper/src/deepseek/DeepSeek.php b/plugin/ai_helper/src/deepseek/DeepSeek.php new file mode 100644 index 000000000..e50494f48 --- /dev/null +++ b/plugin/ai_helper/src/deepseek/DeepSeek.php @@ -0,0 +1,91 @@ +apiKey = $apiKey; + $this->headers = [ + 'Content-Type: application/json', + "Authorization: Bearer {$this->apiKey}", + ]; + } + + /** + * Generate questions using the DeepSeek API. + * + * @param array $payload Data to send to the API + * + * @throws Exception If an error occurs during the request + * + * @return string Decoded response from the API + */ + public function generateQuestions(array $payload): string + { + $url = DeepSeekUrl::completionsUrl(); + $response = $this->sendRequest($url, 'POST', $payload); + + if (empty($response)) { + throw new Exception('The DeepSeek API returned no response.'); + } + + $result = json_decode($response, true); + + // Validate errors returned by the API + if (isset($result['error'])) { + throw new Exception("DeepSeek API Error: {$result['error']['message']}"); + } + + // Ensure the response contains the expected "choices" field + if (!isset($result['choices'][0]['message']['content'])) { + throw new Exception('Unexpected response format from the DeepSeek API.'); + } + + return $result['choices'][0]['message']['content']; + } + + /** + * Send a request to the DeepSeek API. + * + * @param string $url Endpoint to send the request to + * @param string $method HTTP method (e.g., GET, POST) + * @param array $data Data to send as JSON + * + * @throws Exception If a cURL error occurs + * + * @return string Raw response from the API + */ + private function sendRequest(string $url, string $method, array $data = []): string + { + $ch = curl_init($url); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + $errorMessage = curl_error($ch); + curl_close($ch); + throw new Exception("cURL Error: {$errorMessage}"); + } + + curl_close($ch); + + // Validate HTTP status codes + if ($httpCode < 200 || $httpCode >= 300) { + throw new Exception("Request to DeepSeek failed with HTTP status code: {$httpCode}"); + } + + return $response; + } +} diff --git a/plugin/ai_helper/src/deepseek/DeepSeekUrl.php b/plugin/ai_helper/src/deepseek/DeepSeekUrl.php new file mode 100644 index 000000000..5d9b1453d --- /dev/null +++ b/plugin/ai_helper/src/deepseek/DeepSeekUrl.php @@ -0,0 +1,17 @@ +contentTypes = [ + "application/json" => "Content-Type: application/json", + "multipart/form-data" => "Content-Type: multipart/form-data", + ]; + + $this->headers = [ + $this->contentTypes["application/json"], + "Authorization: Bearer $apiKey", + ]; + + if (!empty($organizationId)) { + $this->headers[] = "OpenAI-Organization: $organizationId"; + } + } + + /** + * @return bool|string + */ + public function listModels() + { + $url = OpenAiUrl::fineTuneModel(); + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $model + * + * @return bool|string + */ + public function retrieveModel($model) + { + $model = "/$model"; + $url = OpenAiUrl::fineTuneModel().$model; + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $opts + * @param null $stream + * + * @return bool|string + */ + public function completion(array $opts, callable $stream = null) + { + if ($stream !== null && isset($opts['stream']) && $opts['stream']) { + $this->streamMethod = $stream; + } + + $opts['model'] = $opts['model'] ?? $this->model; + $url = OpenAiUrl::completionsURL(); + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function createEdit($opts) + { + $url = OpenAiUrl::editsUrl(); + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function image($opts) + { + $url = OpenAiUrl::imageUrl()."/generations"; + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function imageEdit($opts) + { + $url = OpenAiUrl::imageUrl()."/edits"; + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function createImageVariation($opts) + { + $url = OpenAiUrl::imageUrl()."/variations"; + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function moderation($opts) + { + $url = OpenAiUrl::moderationUrl(); + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function uploadFile($opts) + { + $url = OpenAiUrl::filesUrl(); + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @return bool|string + */ + public function listFiles() + { + $url = OpenAiUrl::filesUrl(); + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fileId + * + * @return bool|string + */ + public function retrieveFile($fileId) + { + $fileId = "/$fileId"; + $url = OpenAiUrl::filesUrl().$fileId; + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fileId + * + * @return bool|string + */ + public function retrieveFileContent($fileId) + { + $fileId = "/$fileId/content"; + $url = OpenAiUrl::filesUrl().$fileId; + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fileId + * + * @return bool|string + */ + public function deleteFile($fileId) + { + $fileId = "/$fileId"; + $url = OpenAiUrl::filesUrl().$fileId; + + return $this->sendRequest($url, 'DELETE'); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function createFineTune($opts) + { + $url = OpenAiUrl::fineTuneUrl(); + + return $this->sendRequest($url, 'POST', $opts); + } + + /** + * @return bool|string + */ + public function listFineTunes() + { + $url = OpenAiUrl::fineTuneUrl(); + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fineTuneId + * + * @return bool|string + */ + public function retrieveFineTune($fineTuneId) + { + $fineTuneId = "/$fineTuneId"; + $url = OpenAiUrl::fineTuneUrl().$fineTuneId; + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fineTuneId + * + * @return bool|string + */ + public function cancelFineTune($fineTuneId) + { + $fineTuneId = "/$fineTuneId/cancel"; + $url = OpenAiUrl::fineTuneUrl().$fineTuneId; + + return $this->sendRequest($url, 'POST'); + } + + /** + * @param $fineTuneId + * + * @return bool|string + */ + public function listFineTuneEvents($fineTuneId) + { + $fineTuneId = "/$fineTuneId/events"; + $url = OpenAiUrl::fineTuneUrl().$fineTuneId; + + return $this->sendRequest($url, 'GET'); + } + + /** + * @param $fineTuneId + * + * @return bool|string + */ + public function deleteFineTune($fineTuneId) + { + $fineTuneId = "/$fineTuneId"; + $url = OpenAiUrl::fineTuneModel().$fineTuneId; + + return $this->sendRequest($url, 'DELETE'); + } + + /** + * @param $opts + * + * @return bool|string + */ + public function embeddings($opts) + { + $url = OpenAiUrl::embeddings(); + + return $this->sendRequest($url, 'POST', $opts); + } + + public function setTimeout(int $timeout) + { + $this->timeout = $timeout; + } + + private function sendRequest(string $url, string $method, array $opts = []): string + { + $post_fields = json_encode($opts); + + if (isset($opts['file']) || isset($opts['image'])) { + $this->headers[0] = $this->contentTypes["multipart/form-data"]; + $post_fields = $opts; + } else { + $this->headers[0] = $this->contentTypes["application/json"]; + } + $curl_info = [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => $this->timeout, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_POSTFIELDS => $post_fields, + CURLOPT_HTTPHEADER => $this->headers, + ]; + + if (empty($opts)) { + unset($curl_info[CURLOPT_POSTFIELDS]); + } + + if (isset($opts['stream']) && $opts['stream']) { + $curl_info[CURLOPT_WRITEFUNCTION] = $this->streamMethod; + } + + $curl = curl_init(); + + curl_setopt_array($curl, $curl_info); + $response = curl_exec($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + + if (curl_errno($curl)) { + $errorMessage = curl_error($curl); + curl_close($curl); + throw new Exception("cURL Error: {$errorMessage}"); + } + + curl_close($curl); + + if ($httpCode === 429) { + throw new Exception("Insufficient quota. Please check your OpenAI account plan and billing details."); + } + + if ($httpCode < 200 || $httpCode >= 300) { + throw new Exception("HTTP Error: {$httpCode}, Response: {$response}"); + } + + return $response; + } +} diff --git a/plugin/ai_helper/src/openai/OpenAiUrl.php b/plugin/ai_helper/src/openai/OpenAiUrl.php new file mode 100644 index 000000000..1b05ea2a0 --- /dev/null +++ b/plugin/ai_helper/src/openai/OpenAiUrl.php @@ -0,0 +1,74 @@ +generateQuestions($nQ, $lang, $topic, $questionType); + + echo json_encode([ + 'success' => true, + 'text' => trim($resultText), + ]); +} catch (Exception $e) { + error_log("Error: ".$e->getMessage()); + echo json_encode([ + 'success' => false, + 'text' => $e->getMessage(), + ]); +} diff --git a/plugin/ai_helper/tool/learnpath.php b/plugin/ai_helper/tool/learnpath.php new file mode 100644 index 000000000..05f20c8dd --- /dev/null +++ b/plugin/ai_helper/tool/learnpath.php @@ -0,0 +1,218 @@ +getApiList(); +$apiName = $plugin->get('api_name'); + +if (!in_array($apiName, array_keys($apiList))) { + echo json_encode(['success' => false, 'text' => 'AI Provider not available.']); + exit; +} + +$courseLanguage = (string) $_REQUEST['language']; +$chaptersCount = (int) $_REQUEST['nro_items']; +$topic = (string) $_REQUEST['lp_name']; +$wordsCount = (int) $_REQUEST['words_count']; +$courseCode = (string) $_REQUEST['course_code']; +$sessionId = (int) $_REQUEST['session_id']; +$addTests = ('true' === $_REQUEST['add_tests']); +$nQ = ($addTests ? (int) $_REQUEST['nro_questions'] : 0); + +$messageGetItems = 'Generate the table of contents of a course in "%s" in %d or fewer chapters on the topic "%s". Return it as a list of items separated by new lines. Do not include a conclusion chapter.'; +$prompt = sprintf($messageGetItems, $courseLanguage, $chaptersCount, $topic); + +$resultText = $plugin->getCompletionText($prompt, 'learnpath'); + +if (isset($resultText['error']) && $resultText['error']) { + echo json_encode(['success' => false, 'text' => $resultText['message']]); + exit; +} + +if (empty($resultText)) { + echo json_encode(['success' => false, 'text' => 'AI returned no results.']); + exit; +} + +$lpItems = []; +if (!empty($resultText)) { + $style = api_get_css_asset('bootstrap/dist/css/bootstrap.min.css'); + $style .= api_get_css_asset('fontawesome/css/font-awesome.min.css'); + $style .= api_get_css(ChamiloApi::getEditorDocStylePath()); + $style .= api_get_css_asset('ckeditor/plugins/codesnippet/lib/highlight/styles/default.css'); + $style .= api_get_asset('ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js'); + $style .= ''; + + $items = explode("\n", $resultText); + $position = 1; + foreach ($items as $item) { + if (substr($item, 0, 2) === '- ') { + $item = substr($item, 2); + } + $explodedItem = preg_split('/\d\./', $item); + $title = count($explodedItem) > 1 ? $explodedItem[1] : $explodedItem[0]; + if (!empty($title)) { + $lpItems[$position]['title'] = trim($title); + $messageGetItemContent = 'In the context of "%s", generate a document with HTML tags in "%s" with %d words of content or less, about "%s", as to be included as one chapter in a larger document on "%s". Consider the context is established for the reader and you do not need to repeat it.'; + $promptItem = sprintf($messageGetItemContent, $topic, $courseLanguage, $wordsCount, $title, $topic); + $resultContentText = $plugin->getCompletionText($promptItem, 'learnpath'); + if (isset($resultContentText['error']) && $resultContentText['error']) { + continue; + } + $lpItemContent = (!empty($resultContentText) ? trim($resultContentText) : ''); + if (false !== stripos($lpItemContent, '')) { + $lpItemContent = preg_replace("||i", "\r\n$style\r\n\\0", $lpItemContent); + } else { + $lpItemContent = ''.trim($title).''.$style.''.$lpItemContent.''; + } + $lpItems[$position]['content'] = $lpItemContent; + $position++; + } + } +} + +// Create the learnpath and return the id generated. +$return = ['success' => false, 'lp_id' => 0]; +if (!empty($lpItems)) { + $lpId = learnpath::add_lp( + $courseCode, + $topic, + '', + 'chamilo', + 'manual' + ); + + if (!empty($lpId)) { + learnpath::toggle_visibility($lpId, 0); + $courseInfo = api_get_course_info($courseCode); + $lp = new \learnpath( + $courseCode, + $lpId, + api_get_user_id() + ); + $lp->generate_lp_folder($courseInfo, $topic); + $order = 1; + $lpItemsIds = []; + foreach ($lpItems as $dspOrder => $item) { + $documentId = $lp->create_document( + $courseInfo, + $item['content'], + $item['title'], + 'html' + ); + + if (!empty($documentId)) { + $prevDocItem = (isset($lpItemsIds[$order - 1]) ? (int) $lpItemsIds[$order - 1]['item_id'] : 0); + $lpItemId = $lp->add_item( + 0, + $prevDocItem, + 'document', + $documentId, + $item['title'], + '', + 0, + 0, + api_get_user_id(), + $order + ); + $lpItemsIds[$order]['item_id'] = $lpItemId; + $lpItemsIds[$order]['item_type'] = 'document'; + if ($addTests && !empty($lpItemId)) { + $promptQuiz = 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question. Show the question directly without any prefix.'; + $promptQuiz = sprintf($promptQuiz, $nQ, $courseLanguage, $item['title'], $topic); + $resultQuizText = $plugin->getCompletionText($promptQuiz, 'quiz'); + if (!empty($resultQuizText)) { + $request = []; + $request['quiz_name'] = get_lang('Exercise').': '.$item['title']; + $request['nro_questions'] = $nQ; + $request['course_id'] = api_get_course_int_id($courseCode); + $request['aiken_format'] = trim($resultQuizText); + $exerciseId = aikenImportExercise(null, $request); + if (!empty($exerciseId)) { + $order++; + $prevQuizItem = (isset($lpItemsIds[$order - 1]) ? (int) $lpItemsIds[$order - 1]['item_id'] : 0); + $lpQuizItemId = $lp->add_item( + 0, + $prevQuizItem, + 'quiz', + $exerciseId, + $request['quiz_name'], + '', + 0, + 0, + api_get_user_id(), + $order + ); + if (!empty($lpQuizItemId)) { + $maxScore = (float) $nQ; + $minScore = round($nQ / 2, 2); + $lpItemsIds[$order]['item_id'] = $lpQuizItemId; + $lpItemsIds[$order]['item_type'] = 'quiz'; + $lpItemsIds[$order]['min_score'] = $minScore; + $lpItemsIds[$order]['max_score'] = $maxScore; + } + } + } + } + } + $order++; + } + + // Add the final item + if ($addTests) { + $finalTitle = get_lang('EndOfLearningPath'); + $finalContent = file_get_contents(api_get_path(SYS_CODE_PATH).'lp/final_item_template/template.html'); + $finalDocId = $lp->create_document( + $courseInfo, + $finalContent, + $finalTitle + ); + $prevFinalItem = (isset($lpItemsIds[$order - 1]) ? (int) $lpItemsIds[$order - 1]['item_id'] : 0); + $lpFinalItemId = $lp->add_item( + 0, + $prevFinalItem, + TOOL_LP_FINAL_ITEM, + $finalDocId, + $finalTitle, + '', + 0, + 0, + api_get_user_id(), + $order + ); + $lpItemsIds[$order]['item_id'] = $lpFinalItemId; + $lpItemsIds[$order]['item_type'] = TOOL_LP_FINAL_ITEM; + + // Set lp items prerequisites + if (count($lpItemsIds) > 0) { + for ($i = 1; $i <= count($lpItemsIds); $i++) { + $prevIndex = ($i - 1); + if (isset($lpItemsIds[$prevIndex])) { + $itemId = $lpItemsIds[$i]['item_id']; + $prerequisite = $lpItemsIds[$prevIndex]['item_id']; + $minScore = ('quiz' === $lpItemsIds[$prevIndex]['item_type'] ? $lpItemsIds[$prevIndex]['min_score'] : 0); + $maxScore = ('quiz' === $lpItemsIds[$prevIndex]['item_type'] ? $lpItemsIds[$prevIndex]['max_score'] : 100); + $lp->edit_item_prereq($itemId, $prerequisite, $minScore, $maxScore); + } + } + } + } + } + $return = [ + 'success' => true, + 'lp_id' => $lpId, + ]; +} +echo json_encode($return); diff --git a/plugin/ai_helper/uninstall.php b/plugin/ai_helper/uninstall.php new file mode 100644 index 000000000..e9729b1e3 --- /dev/null +++ b/plugin/ai_helper/uninstall.php @@ -0,0 +1,16 @@ +uninstall(); diff --git a/plugin/azure_active_directory/CHANGELOG.md b/plugin/azure_active_directory/CHANGELOG.md new file mode 100644 index 000000000..f2c618ee3 --- /dev/null +++ b/plugin/azure_active_directory/CHANGELOG.md @@ -0,0 +1,48 @@ +# Azure Active Directory Changelog + +## 2.5 - 2024-11-18 + +* Added new options to get the user and groups with delta query (or change tracking) when syncing with scripts. +this requires manually doing the following changes to your database if you are upgrading from v2.4 +```sql +CREATE TABLE azure_ad_sync_state (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, value LONGTEXT NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +``` + +## 2.4 - 2024-08-28 + +* Added a new user extra field to save the unique Azure ID (internal UID). +This requires manually doing the following changes to your database if you are upgrading from v2.3 +```sql +INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (1, 1, 'azure_uid', 'Azure UID (internal ID)', '', 1, null, null, null, null, '2024-08-28 00:00:00'); +``` +* Added a new option to set the order to verify the existing user in Chamilo +```sql +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_existing_user_verification_order', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0); +``` +* Added a new option to update user info during the login proccess. +```sql +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_update_users', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0); +``` +* Added new scripts to syncronize users and groups with users and usergroups (classes). And an option to deactivate accounts in Chamilo that do not exist in Azure. +```sql +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_tenant_id', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0); +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_deactivate_nonexisting_users', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0); +``` + +## 2.3 - 2021-03-30 + +* Added admin, session admin and teacher groups. This requires adding the following fields to your database if + upgrading from a previous version of the plugin manually: +``` +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_group_id_admin', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', null, null, 1, 1, 0); +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_group_id_session_admin', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', null, null, 1, 1, 0); +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_group_id_teacher', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', null, null, 1, 1, 0); +``` + +## 2.2 - 2021-03-02 + +* Added provisioning setting + +## 2.1 - 2020 + +* Initial tested implementation of Azure Active Directory single sign on \ No newline at end of file diff --git a/plugin/azure_active_directory/README.md b/plugin/azure_active_directory/README.md new file mode 100644 index 000000000..460199dc3 --- /dev/null +++ b/plugin/azure_active_directory/README.md @@ -0,0 +1,46 @@ +# The Azure Active Directory Plugin + +This plugin allows users to authenticate (with OAuth2) through Microsoft's Azure Active Directory. +This will modify the login form to either substitute the default login form or add another option to connect through +Azure. +An option allows you to automatically provision/create users in Chamilo from their account on Azure if they don't exist +in Chamilo yet. + +This plugin adds two extra fields for users: + +* `organisationemail`, the email registered in Azure Active Directory for each user (under _Email_ in the _Contact info_ section). +* `azure_id`, to save the internal ID for each user in Azure (which is also the prefix before the _@_ sign in the _User Principal Name_). + +### Prerequisites +This plugin will *not* work if you do not use HTTPS. +Make sure your portal is in HTTPS before you configure this plugin. + +### To configure Azure Active Directory +* Create and configure an application in your Azure panel (Azure Active Directory -> Applications registration -> New registration)). +* In the _Authentication_ section, set an _Reply URL_ (or _Redirect URIs_) of `https://{CHAMILO_URL}/plugin/azure_active_directory/src/callback.php`. +* In the _Front-channel logout URL_, use `https://{CHAMILO_URL}/index.php?logout=logout`. +* Leave the rest of the _Authentication_ section unchanged. +* In _Certificates & secrets_, create a secret string (or application password). Keep the _Value_ field at hand. If you don't copy it somewhere at this point, it will later be hidden, so take a copy, seriously! +* Make sure you actually have users. + +### To configure this plugin +* _Enable_: You can enable the plugin once everything is configured correctly. Disabling it will return to the normal Chamilo login procedure. +* _Application ID_: Enter the _Application (client) ID_ assigned to your app when you created it in your Azure Active Directory interface, under _App registrations_. +* _Application secret_: Enter the client secret _value_ created in _Certificate & secrets_ above. +* _Block name_: (Optional) The name to show above the login button. +* _Force logout button_: (Optional) Add a button to force logout from Azure. +* _Management login_: (Optional) Disable the chamilo login and enable an alternative login page for users. + You will need copy the `/plugin/azure_active_directory/layout/login_form.tpl` file to `/main/template/overrides/layout/` directory. +* _Name for the management login_: A name for the manager login. By default, it is set to "Management Login". +* _Automated provisioning_: Enable if you want users to be created automatically in Chamilo (as students) when they don't exist yet. +* Assign a region in which the login option will appear. Preferably `login_bottom`. + +### Enable through the normal login form +You can configure the external login procedure to work with the classic Chamilo form login. +To do it, make sure users have _azure_ in their auth_source field, then add this line in `configuration.php` file +```php +$extAuthSource["azure"]["login"] = $_configuration['root_sys']."main/auth/external_login/login.azure.php"; +``` + +### Dependencies +> This plugin uses the [`thenetworg/oauth2-azure`](https://github.com/TheNetworg/oauth2-azure) package. diff --git a/plugin/azure_active_directory/index.php b/plugin/azure_active_directory/index.php new file mode 100644 index 000000000..d966e32eb --- /dev/null +++ b/plugin/azure_active_directory/index.php @@ -0,0 +1,38 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ + +// Check if AzureActiveDirectory exists, since this is not loaded as a page. +// index.php is shown as a block when showing the region to which the plugin is assigned +if (class_exists(AzureActiveDirectory::class)) { + /** @var AzureActiveDirectory $activeDirectoryPlugin */ + $activeDirectoryPlugin = AzureActiveDirectory::create(); + + if ($activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_ENABLE) === 'true') { + $_template['block_title'] = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_BLOCK_NAME); + + $_template['signin_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_AUTHORIZE); + + if ('true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_FORCE_LOGOUT_BUTTON)) { + $_template['signout_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_LOGOUT); + } + + $managementLoginEnabled = 'true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE); + + $_template['management_login_enabled'] = $managementLoginEnabled; + + if ($managementLoginEnabled) { + $managementLoginName = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME); + + if (empty($managementLoginName)) { + $managementLoginName = $activeDirectoryPlugin->get_lang('ManagementLogin'); + } + + $_template['management_login_name'] = $managementLoginName; + } + } +} diff --git a/plugin/azure_active_directory/install.php b/plugin/azure_active_directory/install.php new file mode 100644 index 000000000..b1a74509a --- /dev/null +++ b/plugin/azure_active_directory/install.php @@ -0,0 +1,8 @@ +install(); diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php new file mode 100644 index 000000000..22d229cd8 --- /dev/null +++ b/plugin/azure_active_directory/lang/dutch.php @@ -0,0 +1,54 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +$strings['plugin_title'] = 'Azure Active Directory'; +$strings['plugin_comment'] = 'Sta authenticatie met Microsoft\'s Azure Active Directory toe'; + +$strings['enable'] = 'Inschakelen'; +$strings['app_id'] = 'Applicatie ID'; +$strings['app_id_help'] = 'Voeg de Applicatie Id toegewezen aan uw app bij de Azure portal, b.v. 580e250c-8f26-49d0-bee8-1c078add1609'; +$strings['app_secret'] = 'Applicatie gehem'; +$strings['force_logout'] = 'Forceer uitlogknop'; +$strings['force_logout_help'] = 'Toon een knop om afmeldingssessie van Azure af te dwingen.'; +$strings['block_name'] = 'Blok naam'; +$strings['management_login_enable'] = 'Beheer login'; +$strings['management_login_enable_help'] = 'Schakel de chamilo-login uit en schakel een alternatieve inlogpagina in voor gebruikers.
' + .'U zult moeten kopiëren de /plugin/azure_active_directory/layout/login_form.tpl bestand in het /main/template/overrides/layout/ dossier.'; +$strings['management_login_name'] = 'Naam voor de beheeraanmelding'; +$strings['management_login_name_help'] = 'De standaardinstelling is "Beheer login".'; +$strings['existing_user_verification_order'] = 'Existing user verification order'; +$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. ' + .'By default is 1, 2, 3.' + .'
  1. EXTRA_FIELD_ORGANISATION_EMAIL (mail)
  2. EXTRA_FIELD_AZURE_ID (mailNickname)
  3. EXTRA_FIELD_AZURE_UID (id of objectId)
'; +$strings['OrganisationEmail'] = 'Organisatie e-mail'; +$strings['AzureId'] = 'Azure ID (mailNickname)'; +$strings['AzureUid'] = 'Azure UID (internal ID)'; +$strings['ManagementLogin'] = 'Beheer Login'; +$strings['InvalidId'] = 'Deze identificatie is niet geldig (verkeerde log-in of wachtwoord). Errocode: AZMNF'; +$strings['provisioning'] = 'Geautomatiseerde inrichting'; +$strings['update_users'] = 'Update users'; +$strings['update_users_help'] = 'Allow user data to be updated at the start of the session.'; +$strings['provisioning_help'] = 'Maak automatisch nieuwe gebruikers (als studenten) vanuit Azure wanneer ze niet in Chamilo zijn.'; +$strings['group_id_admin'] = 'Groeps-ID voor platformbeheerders'; +$strings['group_id_admin_help'] = 'De groeps-ID is te vinden in de details van de gebruikersgroep en ziet er ongeveer zo uit: ae134eef-cbd4-4a32-ba99-49898a1314b6. Indien leeg, wordt er automatisch geen gebruiker aangemaakt als admin.'; +$strings['group_id_session_admin'] = 'Groeps-ID voor sessiebeheerders'; +$strings['group_id_session_admin_help'] = 'De groeps-ID voor sessiebeheerders. Indien leeg, wordt er automatisch geen gebruiker aangemaakt als sessiebeheerder.'; +$strings['group_id_teacher'] = 'Groeps-ID voor docenten'; +$strings['group_id_teacher_help'] = 'De groeps-ID voor docenten. Indien leeg, wordt er automatisch geen gebruiker aangemaakt als docent.'; +$strings['additional_interaction_required'] = 'Er is aanvullende interactie vereist om u te authenticeren. Log rechtstreeks in via uw authenticatiesysteem en kom dan terug naar deze pagina om in te loggen.'; +$strings['tenant_id'] = 'Mandanten-ID'; +$strings['tenant_id_help'] = 'Required to run scripts.'; +$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users'; +$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.'; +$strings['script_users_delta'] = 'Delta query for users'; +$strings['script_users_delta_help'] = 'Get newly created, updated, or deleted users without having to perform a full read of the entire user collection. By default, is No.'; +$strings['script_usergroups_delta'] = 'Delta query for usergroups'; +$strings['script_usergroups_delta_help'] = 'Get newly created, updated, or deleted groups, including group membership changes, without having to perform a full read of the entire group collection. By default, is No.'; +$strings['group_filter_regex'] = 'Group filter RegEx'; +$strings['group_filter_regex_help'] = 'Regular expression to filter groups (only matches will be synchronized), e.g. .*-FIL-.* .*-PAR-.* .*(FIL|PAR).* ^(FIL|PAR).*'; diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php new file mode 100644 index 000000000..8cfd60969 --- /dev/null +++ b/plugin/azure_active_directory/lang/english.php @@ -0,0 +1,54 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +$strings['plugin_title'] = 'Azure Active Directory'; +$strings['plugin_comment'] = 'Allow authentication with Microsoft\'s Azure Active Directory'; + +$strings['enable'] = 'Enable'; +$strings['app_id'] = 'Application ID'; +$strings['app_id_help'] = 'Enter the Application Id assigned to your app by the Azure portal, e.g. 580e250c-8f26-49d0-bee8-1c078add1609'; +$strings['app_secret'] = 'Application secret'; +$strings['force_logout'] = 'Force logout button'; +$strings['force_logout_help'] = 'Show a button to force logout session from Azure.'; +$strings['block_name'] = 'Block name'; +$strings['management_login_enable'] = 'Management login'; +$strings['management_login_enable_help'] = 'Disable the chamilo login and enable an alternative login page for admin users.
' + .'You will need to copy the /plugin/azure_active_directory/layout/login_form.tpl file to /main/template/overrides/layout/ directory.'; +$strings['management_login_name'] = 'Name for the management login'; +$strings['management_login_name_help'] = 'The default is "Management Login".'; +$strings['existing_user_verification_order'] = 'Existing user verification order'; +$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. ' + .'By default is 1, 2, 3.' + .'
  1. EXTRA_FIELD_ORGANISATION_EMAIL (mail)
  2. EXTRA_FIELD_AZURE_ID (mailNickname)
  3. EXTRA_FIELD_AZURE_UID (id or objectId)
'; +$strings['OrganisationEmail'] = 'Organisation e-mail'; +$strings['AzureId'] = 'Azure ID (mailNickname)'; +$strings['AzureUid'] = 'Azure UID (internal ID)'; +$strings['ManagementLogin'] = 'Management Login'; +$strings['InvalidId'] = 'Login failed - incorrect login or password. Errocode: AZMNF'; +$strings['provisioning'] = 'Automated provisioning'; +$strings['provisioning_help'] = 'Automatically create new users (as students) from Azure when they are not in Chamilo.'; +$strings['update_users'] = 'Update users'; +$strings['update_users_help'] = 'Allow user data to be updated at the start of the session.'; +$strings['group_id_admin'] = 'Group ID for platform admins'; +$strings['group_id_admin_help'] = 'The group ID can be found in the user group details, looking similar to this: ae134eef-cbd4-4a32-ba99-49898a1314b6. If empty, no user will be automatically created as admin.'; +$strings['group_id_session_admin'] = 'Group ID for session admins'; +$strings['group_id_session_admin_help'] = 'The group ID for session admins. If empty, no user will be automatically created as session admin.'; +$strings['group_id_teacher'] = 'Group ID for teachers'; +$strings['group_id_teacher_help'] = 'The group ID for teachers. If empty, no user will be automatically created as teacher.'; +$strings['additional_interaction_required'] = 'Some additional interaction is required to authenticate you. Please login directly through your authentication system, then come back to this page to login.'; +$strings['tenant_id'] = 'Tenant ID'; +$strings['tenant_id_help'] = 'Required to run scripts.'; +$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users'; +$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.'; +$strings['script_users_delta'] = 'Delta query for users'; +$strings['script_users_delta_help'] = 'Get newly created, updated, or deleted users without having to perform a full read of the entire user collection. By default, is No.'; +$strings['script_usergroups_delta'] = 'Delta query for usergroups'; +$strings['script_usergroups_delta_help'] = 'Get newly created, updated, or deleted groups, including group membership changes, without having to perform a full read of the entire group collection. By default, is No.'; +$strings['group_filter_regex'] = 'Group filter RegEx'; +$strings['group_filter_regex_help'] = 'Regular expression to filter groups (only matches will be synchronized), e.g. .*-FIL-.* .*-PAR-.* .*(FIL|PAR).* ^(FIL|PAR).*'; diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php new file mode 100644 index 000000000..d49ce8072 --- /dev/null +++ b/plugin/azure_active_directory/lang/french.php @@ -0,0 +1,54 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +$strings['plugin_title'] = 'Azure Active Directory'; +$strings['plugin_comment'] = 'Permet l\'authentification des utilisateurs via Azure Active Directory de Microsoft'; + +$strings['enable'] = 'Activer'; +$strings['app_id'] = 'ID de l\'application'; +$strings['app_id_help'] = 'Introduisez l\'ID de l\'application assigné à votre app par le portail d\'Azure, p.ex. 580e250c-8f26-49d0-bee8-1c078add1609'; +$strings['app_secret'] = 'Clef secrète de l\'application'; +$strings['force_logout'] = 'Bouton de logout'; +$strings['force_logout_help'] = 'Affiche un bouton pour se délogger d\'Azure.'; +$strings['block_name'] = 'Nom du bloc'; +$strings['management_login_enable'] = 'Login de gestion'; +$strings['management_login_enable_help'] = 'Désactiver le login de Chamilo et permettre une page de login alternative pour les utilisateurs administrateurs.
' + .'Vous devez, pour cela, copier le fichier /plugin/azure_active_directory/layout/login_form.tpl dans le répertoire /main/template/overrides/layout/.'; +$strings['management_login_name'] = 'Nom du login de gestion'; +$strings['management_login_name_help'] = 'Le nom par défaut est "Login de gestion".'; +$strings['existing_user_verification_order'] = 'Existing user verification order'; +$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. ' + .'By default is 1, 2, 3.' + .'
  1. EXTRA_FIELD_ORGANISATION_EMAIL (mail)
  2. EXTRA_FIELD_AZURE_ID (mailNickname)
  3. EXTRA_FIELD_AZURE_UID (id ou objectId)
'; +$strings['OrganisationEmail'] = 'E-mail professionnel'; +$strings['AzureId'] = 'ID Azure (mailNickname)'; +$strings['AzureUid'] = 'Azure UID (internal ID)'; +$strings['ManagementLogin'] = 'Login de gestion'; +$strings['InvalidId'] = 'Échec du login - nom d\'utilisateur ou mot de passe incorrect. Errocode: AZMNF'; +$strings['provisioning'] = 'Création automatisée'; +$strings['provisioning_help'] = 'Créer les utilisateurs automatiquement (en tant qu\'apprenants) depuis Azure s\'ils n\'existent pas encore dans Chamilo.'; +$strings['update_users'] = 'Actualiser les utilisateurs'; +$strings['update_users_help'] = 'Permettre d\'actualiser les données de l\'utilisateur lors du démarrage de la session.'; +$strings['group_id_admin'] = 'ID du groupe administrateur'; +$strings['group_id_admin_help'] = 'L\'id du groupe peut être trouvé dans les détails du groupe, et ressemble à ceci : ae134eef-cbd4-4a32-ba99-49898a1314b6. Si ce champ est laissé vide, aucun utilisateur ne sera créé en tant qu\'administrateur.'; +$strings['group_id_session_admin'] = 'ID du groupe administrateur de sessions'; +$strings['group_id_session_admin_help'] = 'The group ID for session admins. Si ce champ est laissé vide, aucun utilisateur ne sera créé en tant qu\'administrateur de sessions.'; +$strings['group_id_teacher'] = 'ID du groupe enseignant'; +$strings['group_id_teacher_help'] = 'The group ID for teachers. Si ce champ est laissé vide, aucun utilisateur ne sera créé en tant qu\'enseignant.'; +$strings['additional_interaction_required'] = 'Une interaction supplémentaire est nécessaire pour vous authentifier. Veuillez vous connecter directement auprès de votre système d\'authentification, puis revenir ici pour vous connecter.'; +$strings['tenant_id'] = 'ID du client'; +$strings['tenant_id_help'] = 'Nécessaire pour exécuter des scripts.'; +$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users'; +$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.'; +$strings['script_users_delta'] = 'Requête delta pour les utilisateurs'; +$strings['script_users_delta_help'] = 'Get newly created, updated, or deleted users without having to perform a full read of the entire user collection. By default, is No.'; +$strings['script_usergroups_delta'] = 'Requête delta pour les groupes d\'utilisateurs'; +$strings['script_usergroups_delta_help'] = 'Get newly created, updated, or deleted groups, including group membership changes, without having to perform a full read of the entire group collection. By default, is No.'; +$strings['group_filter_regex'] = 'Group filter RegEx'; +$strings['group_filter_regex_help'] = 'Regular expression to filter groups (only matches will be synchronized), e.g. .*-FIL-.* .*-PAR-.* .*(FIL|PAR).* ^(FIL|PAR).*'; diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php new file mode 100644 index 000000000..f2b9f093f --- /dev/null +++ b/plugin/azure_active_directory/lang/spanish.php @@ -0,0 +1,54 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +$strings['plugin_title'] = 'Azure Active Directory'; +$strings['plugin_comment'] = 'Permite la autenticación de usuarios por Azure Active Directory de Microsoft'; + +$strings['enable'] = 'Activar'; +$strings['app_id'] = 'ID de la aplicación'; +$strings['app_id_help'] = 'Introduzca el ID de la aplicación asignado a su app en el portal de Azure, p.ej. 580e250c-8f26-49d0-bee8-1c078add1609'; +$strings['app_secret'] = 'Clave secreta de la aplicación'; +$strings['force_logout'] = 'Botón de logout'; +$strings['force_logout_help'] = 'Muestra un botón para hacer logout de Azure.'; +$strings['block_name'] = 'Nombre del bloque'; +$strings['management_login_enable'] = 'Login de gestión'; +$strings['management_login_enable_help'] = 'Desactivar el login de Chamilo y activar una página de login alternativa para los usuarios de administración.
' + .'Para ello, tendrá que copiar el archivo /plugin/azure_active_directory/layout/login_form.tpl en la carpeta /main/template/overrides/layout/.'; +$strings['management_login_name'] = 'Nombre del bloque de login de gestión'; +$strings['management_login_name_help'] = 'El nombre por defecto es "Login de gestión".'; +$strings['existing_user_verification_order'] = 'Orden de verificación de usuario existente'; +$strings['existing_user_verification_order_help'] = 'Este valor indica el orden en que el usuario serña buscado en Chamilo para verificar su existencia. ' + .'Por defecto es 1, 2, 3.' + .'
  1. EXTRA_FIELD_ORGANISATION_EMAIL (mail)
  2. EXTRA_FIELD_AZURE_ID (mailNickname)
  3. EXTRA_FIELD_AZURE_UID (id o objectId)
'; +$strings['OrganisationEmail'] = 'E-mail profesional'; +$strings['AzureId'] = 'ID Azure (mailNickname)'; +$strings['AzureUid'] = 'UID Azure (ID interno)'; +$strings['ManagementLogin'] = 'Login de gestión'; +$strings['InvalidId'] = 'Problema en el login - nombre de usuario o contraseña incorrecto. Errocode: AZMNF'; +$strings['provisioning'] = 'Creación automatizada'; +$strings['provisioning_help'] = 'Crear usuarios automáticamente (como alumnos) desde Azure si no existen en Chamilo todavía.'; +$strings['update_users'] = 'Actualizar los usuarios'; +$strings['update_users_help'] = 'Permite actualizar los datos del usuario al iniciar sesión.'; +$strings['group_id_admin'] = 'ID de grupo administrador'; +$strings['group_id_admin_help'] = 'El ID de grupo se encuentra en los detalles del grupo en Azure, y parece a: ae134eef-cbd4-4a32-ba99-49898a1314b6. Si deja este campo vacío, ningún usuario será creado como administrador.'; +$strings['group_id_session_admin'] = 'ID de grupo admin de sesiones'; +$strings['group_id_session_admin_help'] = 'El ID de grupo para administradores de sesiones. Si deja este campo vacío, ningún usuario será creado como administrador de sesiones.'; +$strings['group_id_teacher'] = 'ID de grupo profesor'; +$strings['group_id_teacher_help'] = 'El ID de grupo para profesores. Si deja este campo vacío, ningún usuario será creado como profesor.'; +$strings['additional_interaction_required'] = 'Alguna interacción adicional es necesaria para identificarlo/a. Por favor conéctese primero a través de su sistema de autenticación, luego regrese aquí para logearse.'; +$strings['tenant_id'] = 'Id. del inquilino'; +$strings['tenant_id_help'] = 'Necesario para ejecutar scripts.'; +$strings['deactivate_nonexisting_users'] = 'Desactivar usuarios no existentes'; +$strings['deactivate_nonexisting_users_help'] = 'Compara los usuarios registrados en Chamilo con los de Azure y desactiva las cuentas en Chamilo que no existan en Azure.'; +$strings['script_users_delta'] = 'Consula delta para usuarios'; +$strings['script_users_delta_help'] = 'Obtiene usuarios recién creados, actualizados o eliminados sin tener que realizar una lectura completa de toda la colección de usuarios. De forma predeterminada, es No.'; +$strings['script_usergroups_delta'] = 'Consulta delta para grupos de usuarios'; +$strings['script_usergroups_delta_help'] = 'Obtiene grupos recién creados, actualizados o eliminados, incluidos los cambios de membresía del grupo, sin tener que realizar una lectura completa de toda la colección de grupos. De forma predeterminada, es No'; +$strings['group_filter_regex'] = 'Group filter RegEx'; +$strings['group_filter_regex_help'] = 'Expresión regular para filtrar grupos (solo las coincidencias serán sincronizadas), p.ej. .*-FIL-.* .*-PAR-.* .*(FIL|PAR).* ^(FIL|PAR).*'; diff --git a/plugin/azure_active_directory/layout/login_form.tpl b/plugin/azure_active_directory/layout/login_form.tpl new file mode 100644 index 000000000..498422b23 --- /dev/null +++ b/plugin/azure_active_directory/layout/login_form.tpl @@ -0,0 +1,51 @@ +{% if _u.logged == 0 %} + {% if login_form %} +
+
+ {{ login_language_form }} + {% if plugin_login_top is not null %} +
+ {{ plugin_login_top }} +
+ {% endif %} + + {{ login_failed }} + + {% set azure_plugin_enabled = 'azure_active_directory'|api_get_plugin_setting('enable') %} + {% set azure_plugin_manage_login = 'azure_active_directory'|api_get_plugin_setting('manage_login_enable') %} + + {% if 'false' == azure_plugin_enabled or 'false' == azure_plugin_manage_login %} + {{ login_form }} + + {% if "allow_lostpassword" | api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %} + + {% endif %} + + {% endif %} + + {% if plugin_login_bottom is not null %} +
+ {{ plugin_login_bottom }} +
+ {% endif %} +
+
+ {% endif %} +{% endif %} diff --git a/plugin/azure_active_directory/login.php b/plugin/azure_active_directory/login.php new file mode 100644 index 000000000..a6f790f4b --- /dev/null +++ b/plugin/azure_active_directory/login.php @@ -0,0 +1,35 @@ +get(AzureActiveDirectory::SETTING_ENABLE); +$managementLoginEnabled = $plugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE); + +if ('true' !== $pluginEnabled || 'true' !== $managementLoginEnabled) { + header('Location: '.api_get_path(WEB_PATH)); + + exit; +} + +$userId = api_get_user_id(); + +if (!($userId) || api_is_anonymous($userId)) { + $managementLoginName = $plugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME); + + if (empty($managementLoginName)) { + $managementLoginName = $plugin->get_lang('ManagementLogin'); + } + + $template = new Template($managementLoginName); + // Only display if the user isn't logged in. + $template->assign('login_language_form', api_display_language_form(true, true)); + $template->assign('login_form', $template->displayLoginForm()); + + $content = $template->fetch('azure_active_directory/view/login.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); +} diff --git a/plugin/azure_active_directory/plugin.php b/plugin/azure_active_directory/plugin.php new file mode 100644 index 000000000..c9f699a73 --- /dev/null +++ b/plugin/azure_active_directory/plugin.php @@ -0,0 +1,10 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +$plugin_info = AzureActiveDirectory::create()->get_info(); + +$plugin_info['templates'] = ['view/block.tpl']; diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php new file mode 100644 index 000000000..53cfdc5ff --- /dev/null +++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php @@ -0,0 +1,442 @@ + + * + * @package chamilo.plugin.azure_active_directory + */ +class AzureActiveDirectory extends Plugin +{ + public const SETTING_ENABLE = 'enable'; + public const SETTING_APP_ID = 'app_id'; + public const SETTING_APP_SECRET = 'app_secret'; + public const SETTING_BLOCK_NAME = 'block_name'; + public const SETTING_FORCE_LOGOUT_BUTTON = 'force_logout'; + public const SETTING_MANAGEMENT_LOGIN_ENABLE = 'management_login_enable'; + public const SETTING_MANAGEMENT_LOGIN_NAME = 'management_login_name'; + public const SETTING_PROVISION_USERS = 'provisioning'; + public const SETTING_UPDATE_USERS = 'update_users'; + public const SETTING_GROUP_ID_ADMIN = 'group_id_admin'; + public const SETTING_GROUP_ID_SESSION_ADMIN = 'group_id_session_admin'; + public const SETTING_GROUP_ID_TEACHER = 'group_id_teacher'; + public const SETTING_EXISTING_USER_VERIFICATION_ORDER = 'existing_user_verification_order'; + public const SETTING_TENANT_ID = 'tenant_id'; + public const SETTING_DEACTIVATE_NONEXISTING_USERS = 'deactivate_nonexisting_users'; + public const SETTING_GET_USERS_DELTA = 'script_users_delta'; + public const SETTING_GET_USERGROUPS_DELTA = 'script_usergroups_delta'; + public const SETTING_GROUP_FILTER = 'group_filter_regex'; + + public const URL_TYPE_AUTHORIZE = 'login'; + public const URL_TYPE_LOGOUT = 'logout'; + + public const EXTRA_FIELD_ORGANISATION_EMAIL = 'organisationemail'; + public const EXTRA_FIELD_AZURE_ID = 'azure_id'; + public const EXTRA_FIELD_AZURE_UID = 'azure_uid'; + + public const API_PAGE_SIZE = 999; + + /** + * AzureActiveDirectory constructor. + */ + protected function __construct() + { + $settings = [ + self::SETTING_ENABLE => 'boolean', + self::SETTING_APP_ID => 'text', + self::SETTING_APP_SECRET => 'text', + self::SETTING_BLOCK_NAME => 'text', + self::SETTING_FORCE_LOGOUT_BUTTON => 'boolean', + self::SETTING_MANAGEMENT_LOGIN_ENABLE => 'boolean', + self::SETTING_MANAGEMENT_LOGIN_NAME => 'text', + self::SETTING_PROVISION_USERS => 'boolean', + self::SETTING_UPDATE_USERS => 'boolean', + self::SETTING_GROUP_ID_ADMIN => 'text', + self::SETTING_GROUP_ID_SESSION_ADMIN => 'text', + self::SETTING_GROUP_ID_TEACHER => 'text', + self::SETTING_EXISTING_USER_VERIFICATION_ORDER => 'text', + self::SETTING_TENANT_ID => 'text', + self::SETTING_DEACTIVATE_NONEXISTING_USERS => 'boolean', + self::SETTING_GET_USERS_DELTA => 'boolean', + self::SETTING_GET_USERGROUPS_DELTA => 'boolean', + self::SETTING_GROUP_FILTER => 'text', + ]; + + parent::__construct('2.5', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings); + } + + /** + * Instance the plugin. + * + * @staticvar null $result + * + * @return $this + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @return string + */ + public function get_name() + { + return 'azure_active_directory'; + } + + public function getProvider(): Azure + { + return new Azure([ + 'clientId' => $this->get(self::SETTING_APP_ID), + 'clientSecret' => $this->get(self::SETTING_APP_SECRET), + 'redirectUri' => api_get_path(WEB_PLUGIN_PATH).'azure_active_directory/src/callback.php', + 'urlAPI' => 'https://graph.microsoft.com/v1.0/', + 'resource' => 'https://graph.microsoft.com', + ]); + } + + public function getProviderForApiGraph(): Azure + { + $provider = $this->getProvider(); + $provider->tenant = $this->get(AzureActiveDirectory::SETTING_TENANT_ID); + $provider->authWithResource = false; + + return $provider; + } + + /** + * @param string $urlType Type of URL to generate + * + * @return string + */ + public function getUrl($urlType) + { + if (self::URL_TYPE_LOGOUT === $urlType) { + $provider = $this->getProvider(); + + return $provider->getLogoutUrl( + api_get_path(WEB_PATH) + ); + } + + return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php'; + } + + /** + * Create extra fields for user when installing. + * + * @throws ToolsException + */ + public function install() + { + UserManager::create_extra_field( + self::EXTRA_FIELD_ORGANISATION_EMAIL, + ExtraField::FIELD_TYPE_TEXT, + $this->get_lang('OrganisationEmail'), + '' + ); + UserManager::create_extra_field( + self::EXTRA_FIELD_AZURE_ID, + ExtraField::FIELD_TYPE_TEXT, + $this->get_lang('AzureId'), + '' + ); + UserManager::create_extra_field( + self::EXTRA_FIELD_AZURE_UID, + ExtraField::FIELD_TYPE_TEXT, + $this->get_lang('AzureUid'), + '' + ); + + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist(['azure_ad_sync_state'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(AzureSyncState::class), + ] + ); + } + + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist(['azure_ad_sync_state'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(AzureSyncState::class), + ] + ); + } + + public function getExistingUserVerificationOrder(): array + { + $defaultOrder = [1, 2, 3]; + + $settingValue = $this->get(self::SETTING_EXISTING_USER_VERIFICATION_ORDER); + $selectedOrder = array_filter( + array_map( + 'trim', + explode(',', $settingValue) + ) + ); + $selectedOrder = array_map('intval', $selectedOrder); + $selectedOrder = array_filter( + $selectedOrder, + function ($position) use ($defaultOrder): bool { + return in_array($position, $defaultOrder); + } + ); + + if ($selectedOrder) { + return $selectedOrder; + } + + return $defaultOrder; + } + + public function getUserIdByVerificationOrder(array $azureUserData): ?int + { + $selectedOrder = $this->getExistingUserVerificationOrder(); + + $extraFieldValue = new ExtraFieldValue('user'); + $positionsAndFields = [ + 1 => $extraFieldValue->get_item_id_from_field_variable_and_field_value( + AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL, + $azureUserData['mail'] + ), + 2 => $extraFieldValue->get_item_id_from_field_variable_and_field_value( + AzureActiveDirectory::EXTRA_FIELD_AZURE_ID, + $azureUserData['mailNickname'] + ), + 3 => $extraFieldValue->get_item_id_from_field_variable_and_field_value( + AzureActiveDirectory::EXTRA_FIELD_AZURE_UID, + $azureUserData['id'] + ), + ]; + + foreach ($selectedOrder as $position) { + if (!empty($positionsAndFields[$position]) && isset($positionsAndFields[$position]['item_id'])) { + return (int) $positionsAndFields[$position]['item_id']; + } + } + + return null; + } + + /** + * @throws Exception + */ + public function registerUser(array $azureUserInfo) + { + if (empty($azureUserInfo)) { + throw new Exception('Groups info not found.'); + } + + $userId = $this->getUserIdByVerificationOrder($azureUserInfo); + + if (empty($userId)) { + // If we didn't find the user + if ($this->get(self::SETTING_PROVISION_USERS) !== 'true') { + throw new Exception('User not found when checking the extra fields from '.$azureUserInfo['mail'].' or '.$azureUserInfo['mailNickname'].' or '.$azureUserInfo['id'].'.'); + } + + [ + $firstNme, + $lastName, + $username, + $email, + $phone, + $authSource, + $active, + $extra, + ] = $this->formatUserData($azureUserInfo); + + // If the option is set to create users, create it + $userId = UserManager::create_user( + $firstNme, + $lastName, + STUDENT, + $email, + $username, + '', + null, + null, + $phone, + null, + $authSource, + null, + $active, + null, + $extra, + null, + null + ); + + if (!$userId) { + throw new Exception(get_lang('UserNotAdded').' '.$azureUserInfo['userPrincipalName']); + } + + return $userId; + } + + if ($this->get(self::SETTING_UPDATE_USERS) === 'true') { + [ + $firstNme, + $lastName, + $username, + $email, + $phone, + $authSource, + $active, + $extra, + ] = $this->formatUserData($azureUserInfo); + + $userId = UserManager::update_user( + $userId, + $firstNme, + $lastName, + $username, + '', + $authSource, + $email, + STUDENT, + null, + $phone, + null, + null, + $active, + null, + 0, + $extra + ); + + if (!$userId) { + throw new Exception(get_lang('CouldNotUpdateUser').' '.$azureUserInfo['userPrincipalName']); + } + } + + return $userId; + } + + /** + * @return array + */ + public function getGroupUidByRole(): array + { + $groupUidList = [ + 'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN), + 'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN), + 'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER), + ]; + + return array_filter($groupUidList); + } + + /** + * @return array + */ + public function getUpdateActionByRole(): array + { + return [ + 'admin' => function (User $user) { + $user->setStatus(COURSEMANAGER); + + UserManager::addUserAsAdmin($user, false); + }, + 'sessionAdmin' => function (User $user) { + $user->setStatus(SESSIONADMIN); + + UserManager::removeUserAdmin($user, false); + }, + 'teacher' => function (User $user) { + $user->setStatus(COURSEMANAGER); + + UserManager::removeUserAdmin($user, false); + }, + ]; + } + + public function getSyncState(string $title): ?AzureSyncState + { + $stateRepo = Database::getManager()->getRepository(AzureSyncState::class); + + return $stateRepo->findOneBy(['title' => $title]); + } + + public function saveSyncState(string $title, $value) + { + $state = $this->getSyncState($title); + + if (!$state) { + $state = new AzureSyncState(); + $state->setTitle($title); + + Database::getManager()->persist($state); + } + + $state->setValue($value); + + Database::getManager()->flush(); + } + + /** + * @throws Exception + */ + private function formatUserData(array $azureUserInfo): array + { + $phone = null; + + if (isset($azureUserInfo['telephoneNumber'])) { + $phone = $azureUserInfo['telephoneNumber']; + } elseif (isset($azureUserInfo['businessPhones'][0])) { + $phone = $azureUserInfo['businessPhones'][0]; + } elseif (isset($azureUserInfo['mobilePhone'])) { + $phone = $azureUserInfo['mobilePhone']; + } + + // If the option is set to create users, create it + $firstNme = $azureUserInfo['givenName']; + $lastName = $azureUserInfo['surname']; + $email = $azureUserInfo['mail']; + $username = $azureUserInfo['userPrincipalName']; + $authSource = 'azure'; + $active = ($azureUserInfo['accountEnabled'] ? 1 : 0); + $extra = [ + 'extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL => $azureUserInfo['mail'], + 'extra_'.self::EXTRA_FIELD_AZURE_ID => $azureUserInfo['mailNickname'], + 'extra_'.self::EXTRA_FIELD_AZURE_UID => $azureUserInfo['id'], + ]; + + return [ + $firstNme, + $lastName, + $username, + $email, + $phone, + $authSource, + $active, + $extra, + ]; + } +} diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php new file mode 100644 index 000000000..b70c85e21 --- /dev/null +++ b/plugin/azure_active_directory/src/AzureCommand.php @@ -0,0 +1,239 @@ +plugin = AzureActiveDirectory::create(); + $this->plugin->get_settings(true); + $this->provider = $this->plugin->getProviderForApiGraph(); + } + + /** + * @throws IdentityProviderException + */ + protected function generateOrRefreshToken(?AccessTokenInterface &$token) + { + if (!$token || ($token->getExpires() && !$token->getRefreshToken())) { + $token = $this->provider->getAccessToken( + 'client_credentials', + ['resource' => $this->provider->resource] + ); + } + } + + /** + * @throws Exception + * + * @return Generator> + */ + protected function getAzureUsers(): Generator + { + $userFields = [ + 'givenName', + 'surname', + 'mail', + 'userPrincipalName', + 'businessPhones', + 'mobilePhone', + 'accountEnabled', + 'mailNickname', + 'id', + ]; + + $getUsersDelta = 'true' === $this->plugin->get(AzureActiveDirectory::SETTING_GET_USERS_DELTA); + + if ($getUsersDelta) { + $usersDeltaLink = $this->plugin->getSyncState(AzureSyncState::USERS_DATALINK); + + $query = $usersDeltaLink + ? $usersDeltaLink->getValue() + : sprintf('$select=%s', implode(',', $userFields)); + } else { + $query = sprintf( + '$top=%d&$select=%s', + AzureActiveDirectory::API_PAGE_SIZE, + implode(',', $userFields) + ); + } + + $token = null; + + do { + $this->generateOrRefreshToken($token); + + try { + $azureUsersRequest = $this->provider->request( + 'get', + $getUsersDelta ? "users/delta?$query" : "users?$query", + $token + ); + } catch (Exception $e) { + throw new Exception('Exception when requesting users from Azure: '.$e->getMessage()); + } + + $azureUsersInfo = $azureUsersRequest['value'] ?? []; + + foreach ($azureUsersInfo as $azureUserInfo) { + $azureUserInfo['mail'] = $azureUserInfo['mail'] ?? null; + $azureUserInfo['surname'] = $azureUserInfo['surname'] ?? null; + $azureUserInfo['givenName'] = $azureUserInfo['givenName'] ?? null; + + yield $azureUserInfo; + } + + $hasNextLink = false; + + if (!empty($azureUsersRequest['@odata.nextLink'])) { + $hasNextLink = true; + $query = parse_url($azureUsersRequest['@odata.nextLink'], PHP_URL_QUERY); + } + + if ($getUsersDelta && !empty($azureUsersRequest['@odata.deltaLink'])) { + $this->plugin->saveSyncState( + AzureSyncState::USERS_DATALINK, + parse_url($azureUsersRequest['@odata.deltaLink'], PHP_URL_QUERY), + ); + } + } while ($hasNextLink); + } + + /** + * @throws Exception + * + * @return Generator> + */ + protected function getAzureGroups(): Generator + { + $groupFilter = $this->plugin->get(AzureActiveDirectory::SETTING_GROUP_FILTER); + + $groupFields = [ + 'id', + 'displayName', + 'description', + ]; + + $getUsergroupsDelta = 'true' === $this->plugin->get(AzureActiveDirectory::SETTING_GET_USERGROUPS_DELTA); + + if ($getUsergroupsDelta) { + $usergroupsDeltaLink = $this->plugin->getSyncState(AzureSyncState::USERGROUPS_DATALINK); + + $query = $usergroupsDeltaLink + ? $usergroupsDeltaLink->getValue() + : sprintf('$select=%s', implode(',', $groupFields)); + } else { + $query = sprintf( + '$top=%d&$select=%s', + AzureActiveDirectory::API_PAGE_SIZE, + implode(',', $groupFields) + ); + } + + $token = null; + + do { + $this->generateOrRefreshToken($token); + + try { + $azureGroupsRequest = $this->provider->request( + 'get', + $getUsergroupsDelta ? "groups/delta?$query" : "groups?$query", + $token + ); + } catch (Exception $e) { + throw new Exception('Exception when requesting groups from Azure: '.$e->getMessage()); + } + + $azureGroupsInfo = $azureGroupsRequest['value'] ?? []; + + foreach ($azureGroupsInfo as $azureGroupInfo) { + if (!empty($groupFilter) && + !preg_match("/$groupFilter/", $azureGroupInfo['displayName']) + ) { + continue; + } + + yield $azureGroupInfo; + } + + $hasNextLink = false; + + if (!empty($azureGroupsRequest['@odata.nextLink'])) { + $hasNextLink = true; + $query = parse_url($azureGroupsRequest['@odata.nextLink'], PHP_URL_QUERY); + } + + if ($getUsergroupsDelta && !empty($azureGroupsRequest['@odata.deltaLink'])) { + $this->plugin->saveSyncState( + AzureSyncState::USERGROUPS_DATALINK, + parse_url($azureGroupsRequest['@odata.deltaLink'], PHP_URL_QUERY), + ); + } + } while ($hasNextLink); + } + + /** + * @throws Exception + * + * @return Generator> + */ + protected function getAzureGroupMembers(string $groupUid): Generator + { + $userFields = [ + 'mail', + 'mailNickname', + 'id', + ]; + + $query = sprintf( + '$top=%d&$select=%s', + AzureActiveDirectory::API_PAGE_SIZE, + implode(',', $userFields) + ); + + $token = null; + + do { + $this->generateOrRefreshToken($token); + + try { + $azureGroupMembersRequest = $this->provider->request( + 'get', + "groups/$groupUid/members?$query", + $token + ); + } catch (Exception $e) { + throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage()); + } + + $azureGroupMembers = $azureGroupMembersRequest['value'] ?? []; + + foreach ($azureGroupMembers as $azureGroupMember) { + yield $azureGroupMember; + } + + $hasNextLink = false; + + if (!empty($azureGroupMembersRequest['@odata.nextLink'])) { + $hasNextLink = true; + $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY); + } + } while ($hasNextLink); + } +} diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php new file mode 100644 index 000000000..a22169403 --- /dev/null +++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php @@ -0,0 +1,74 @@ + + */ + public function __invoke(): Generator + { + yield 'Synchronizing groups from Azure.'; + + $usergroup = new UserGroup(); + + $groupIdByUid = []; + + foreach ($this->getAzureGroups() as $azureGroupInfo) { + if ($usergroup->usergroup_exists($azureGroupInfo['displayName'])) { + $groupId = $usergroup->getIdByName($azureGroupInfo['displayName']); + + if ($groupId) { + $usergroup->subscribe_users_to_usergroup($groupId, []); + + yield sprintf('Class exists, all users unsubscribed: %s', $azureGroupInfo['displayName']); + } + } else { + $groupId = $usergroup->save([ + 'name' => $azureGroupInfo['displayName'], + 'description' => $azureGroupInfo['description'], + ]); + + if ($groupId) { + yield sprintf('Class created: %s', $azureGroupInfo['displayName']); + } + } + + $groupIdByUid[$azureGroupInfo['id']] = $groupId; + } + + yield '----------------'; + yield 'Subscribing users to groups'; + + foreach ($groupIdByUid as $azureGroupUid => $groupId) { + $newGroupMembers = []; + + yield sprintf('Obtaining members for group (ID %d)', $groupId); + + try { + foreach ($this->getAzureGroupMembers($azureGroupUid) as $azureGroupMember) { + if ($userId = $this->plugin->getUserIdByVerificationOrder($azureGroupMember)) { + $newGroupMembers[] = $userId; + } + } + } catch (Exception $e) { + yield $e->getMessage(); + + continue; + } + + if ($newGroupMembers) { + $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers); + + yield sprintf( + 'User IDs subscribed in class (ID %d): %s', + $groupId, + implode(', ', $newGroupMembers) + ); + } + } + } +} diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php new file mode 100644 index 000000000..61a2123f4 --- /dev/null +++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php @@ -0,0 +1,100 @@ + + */ + public function __invoke(): Generator + { + yield 'Synchronizing users from Azure.'; + + /** @var array $azureCreatedUserIdList */ + $azureCreatedUserIdList = []; + + foreach ($this->getAzureUsers() as $azureUserInfo) { + try { + $userId = $this->plugin->registerUser($azureUserInfo); + } catch (Exception $e) { + yield $e->getMessage(); + + continue; + } + + $azureCreatedUserIdList[$azureUserInfo['id']] = $userId; + + yield sprintf('User (ID %d) with received info: %s ', $userId, serialize($azureUserInfo)); + } + + yield '----------------'; + yield 'Updating users status'; + + $roleGroups = $this->plugin->getGroupUidByRole(); + $roleActions = $this->plugin->getUpdateActionByRole(); + + $userManager = UserManager::getManager(); + $em = Database::getManager(); + + foreach ($roleGroups as $userRole => $groupUid) { + try { + $azureGroupMembersInfo = iterator_to_array($this->getAzureGroupMembers($groupUid)); + } catch (Exception $e) { + yield $e->getMessage(); + + continue; + } + + $azureGroupMembersUids = array_column($azureGroupMembersInfo, 'id'); + + foreach ($azureGroupMembersUids as $azureGroupMembersUid) { + $userId = $azureCreatedUserIdList[$azureGroupMembersUid] ?? null; + + if (!$userId) { + continue; + } + + if (isset($roleActions[$userRole])) { + /** @var User $user */ + $user = $userManager->find($userId); + + $roleActions[$userRole]($user); + + yield sprintf('User (ID %d) status %s', $userId, $userRole); + } + } + + $em->flush(); + } + + if ('true' === $this->plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS) + && 'true' !== $this->plugin->get(AzureActiveDirectory::SETTING_GET_USERS_DELTA) + ) { + yield '----------------'; + + yield 'Trying deactivate non-existing users in Azure'; + + $users = UserManager::getRepository()->findByAuthSource('azure'); + $chamiloUserIdList = array_map( + function ($user) { + return $user->getId(); + }, + $users + ); + + $nonExistingUsers = array_diff($chamiloUserIdList, $azureCreatedUserIdList); + + UserManager::deactivate_users($nonExistingUsers); + + yield sprintf( + 'Deactivated users IDs: %s', + implode(', ', $nonExistingUsers) + ); + } + } +} diff --git a/plugin/azure_active_directory/src/Entity/AzureSyncState.php b/plugin/azure_active_directory/src/Entity/AzureSyncState.php new file mode 100644 index 000000000..bda48f6fd --- /dev/null +++ b/plugin/azure_active_directory/src/Entity/AzureSyncState.php @@ -0,0 +1,68 @@ +id; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): AzureSyncState + { + $this->title = $title; + + return $this; + } + + public function getValue(): string + { + return $this->value; + } + + public function setValue(string $value): AzureSyncState + { + $this->value = $value; + + return $this; + } +} diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php new file mode 100644 index 000000000..2c2eef353 --- /dev/null +++ b/plugin/azure_active_directory/src/callback.php @@ -0,0 +1,166 @@ +get(AzureActiveDirectory::SETTING_ENABLE)) { + api_not_allowed(true); +} + +$provider = $plugin->getProvider(); + +if (!isset($_GET['code'])) { + // If we don't have an authorization code then get one by redirecting + // users to Azure (with the callback URL information) + $authUrl = $provider->getAuthorizationUrl(); + + ChamiloSession::write('oauth2state', $provider->getState()); + + header('Location: '.$authUrl); + exit; +} + +// Check given state against previously stored one to mitigate CSRF attack +if (empty($_GET['state']) || ($_GET['state'] !== ChamiloSession::read('oauth2state'))) { + ChamiloSession::erase('oauth2state'); + + exit; +} + +// Try to get an access token (using the authorization code grant) +try { + $token = $provider->getAccessToken('authorization_code', [ + 'code' => $_GET['code'], + ]); +} catch (Exception $exception) { + if ($exception->getMessage() == 'interaction_required') { + $message = Display::return_message($plugin->get_lang('additional_interaction_required'), 'error', false); + } else { + $message = Display::return_message($exception->getMessage(), 'error'); + } + Display::addFlash($message); + header('Location: '.api_get_path(WEB_PATH)); + exit; +} + +$me = null; + +try { + $userFields = [ + 'givenName', + 'surname', + 'mail', + 'userPrincipalName', + 'businessPhones', + 'mobilePhone', + 'accountEnabled', + 'mailNickname', + 'id', + ]; + + $querySelect = implode(',', $userFields); + + $me = $provider->get( + sprintf('me?$select=%s', $querySelect), + $token + ); + + if (empty($me)) { + throw new Exception('Token not found.'); + } + + // We use the e-mail to authenticate the user, so check that at least one + // e-mail source exists + if (empty($me['mail'])) { + throw new Exception('The mail field is empty in Azure AD and is needed to set the organisation email for this user.'); + } + if (empty($me['mailNickname'])) { + throw new Exception('The mailNickname field is empty in Azure AD and is needed to set the unique username for this user.'); + } + if (empty($me['id'])) { + throw new Exception('The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.'); + } + + $userId = $plugin->registerUser($me); + + if ($roleGroups = $plugin->getGroupUidByRole()) { + $roleActions = $plugin->getUpdateActionByRole(); + /** @var User $user */ + $user = UserManager::getManager()->find($userId); + + $azureGroups = $provider->get('me/memberOf', $token); + + foreach ($roleGroups as $userRole => $groupUid) { + foreach ($azureGroups as $azureGroup) { + $azureGroupUid = $azureGroup['id']; + if ($azureGroupUid === $groupUid) { + $roleActions[$userRole]($user); + + break 2; + } + } + } + + Database::getManager()->flush(); + } + + $userInfo = api_get_user_info($userId); + + /* @TODO add support if user exists in another URL but is validated in this one, add the user to access_url_rel_user */ + + if (empty($userInfo)) { + throw new Exception('User '.$userId.' not found.'); + } + + if ($userInfo['active'] != '1') { + throw new Exception(get_lang('AccountInactive')); + } +} catch (Exception $exception) { + $message = Display::return_message($exception->getMessage(), 'error'); + Display::addFlash($message); + header('Location: '.api_get_path(WEB_PATH)); + exit; +} + +$userInfo['uidReset'] = true; + +$_GET['redirect_after_not_allow_page'] = 1; + +$redirectAfterNotAllowPage = ChamiloSession::read('redirect_after_not_allow_page'); + +ChamiloSession::clear(); + +ChamiloSession::write('redirect_after_not_allow_page', $redirectAfterNotAllowPage); + +ChamiloSession::write('_user', $userInfo); +ChamiloSession::write('_user_auth_source', 'azure_active_directory'); +Event::eventLogin($userInfo['user_id']); + +$GLOBALS['noredirection'] = false; + +Redirect::session_request_uri(true, $userInfo['user_id']); diff --git a/plugin/azure_active_directory/src/scripts/sync_usergroups.php b/plugin/azure_active_directory/src/scripts/sync_usergroups.php new file mode 100644 index 000000000..8ad128c5d --- /dev/null +++ b/plugin/azure_active_directory/src/scripts/sync_usergroups.php @@ -0,0 +1,21 @@ +getMessage()); +} diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php new file mode 100644 index 000000000..33eb4768e --- /dev/null +++ b/plugin/azure_active_directory/src/scripts/sync_users.php @@ -0,0 +1,21 @@ +getMessage()); +} diff --git a/plugin/azure_active_directory/uninstall.php b/plugin/azure_active_directory/uninstall.php new file mode 100644 index 000000000..43a1f4fa7 --- /dev/null +++ b/plugin/azure_active_directory/uninstall.php @@ -0,0 +1,9 @@ +uninstall(); diff --git a/plugin/azure_active_directory/view/block.tpl b/plugin/azure_active_directory/view/block.tpl new file mode 100644 index 000000000..af7928256 --- /dev/null +++ b/plugin/azure_active_directory/view/block.tpl @@ -0,0 +1,22 @@ +{% if not _u.logged %} +
+ {% if not azure_active_directory.block_title is empty %} +

{{ azure_active_directory.block_title }}

+ {% endif %} + + {% if not azure_active_directory.signin_url is empty %} + {{ 'SignIn'|get_lang }} + {% endif %} + + {% if not azure_active_directory.signout_url is empty %} + {{ 'Logout'|get_lang }} + {% endif %} + + {% if azure_active_directory.management_login_enabled %} +
+ + {{ azure_active_directory.management_login_name }} + + {% endif %} +
+{% endif %} diff --git a/plugin/azure_active_directory/view/login.tpl b/plugin/azure_active_directory/view/login.tpl new file mode 100644 index 000000000..89810414a --- /dev/null +++ b/plugin/azure_active_directory/view/login.tpl @@ -0,0 +1,26 @@ +
+
+ {{ login_language_form }} + + {{ login_form }} + + {% if "allow_lostpassword"|api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %} + + {% endif %} +
+
diff --git a/plugin/bbb/CHANGELOG.md b/plugin/bbb/CHANGELOG.md new file mode 100644 index 000000000..97d33a358 --- /dev/null +++ b/plugin/bbb/CHANGELOG.md @@ -0,0 +1,61 @@ +Version 2.11 - 2022-04 +---------------------- +* Add option to close all rooms on all campus on a multi-url enviroment + +Version 2.10 - 2021-10 +---------------------- +* Add support for multiple recording formats + +Version 2.9 - 2021-08 +--------------------- +* Remove interface option (HTML5/Flash) + +Version 2.8 - 2019-07 +--------------------- +* Add rooms + internal meeting id + +Version 2.7 - 2018-07 +--------------------- +* Add interface option (HTML5 or Flash) + +Version 2.6 - 2017-05 +--------------------- +* Add max users limit + +Version 2.5 - 2016-07 +--------------------- +* User global conference support + +Version 2.4 - 2016-05 +------------------------ + +* Global conference support (Requires to update the plugin settings). +* Course group conference support + +Version 2.3 - 2015-05-18 +------------------------ +Changes: +* Added support for variable voiceBridge to be sent on meeting creation. See: +https://code.google.com/p/bigbluebutton/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Component%20Summary&groupby=&sort=&id=1186 +and https://support.chamilo.org/issues/7669 for details. + +Version 2.2 - 2014-10-15 +------------------------ +Changes: +* Add a pseudo-random guid to avoid clashing conferences when several Chamilo portals use the same server. +* Add possibility to hide recordings of previous conferences from the list. +* Show action icons in the action column +* Hide the ID of the meeting (this was an internal ID, useless to the final user). It is still in the HTML source, however +* Show number of minutes of the recording (in the recordings list) + +Version 2.1 +----------- +Released with: Chamilo LMS 1.9.8 +* Session support + +Version 2.0 +----------- + +Version 1.0 +----------- +Released with: Chamilo LMS 1.9.0 diff --git a/plugin/bbb/README.md b/plugin/bbb/README.md new file mode 100644 index 000000000..16e0894e0 --- /dev/null +++ b/plugin/bbb/README.md @@ -0,0 +1,95 @@ +BigBlueButton Chamilo plugin +============================ +This plugin allows you to have videoconference rooms in each course. +It requires you to have a BigBlueButton videoconference server installed on another server (ideally). +Check www.bigbluebutton.org for more about BigBlueButton. + +## Migrating to Chamilo LMS 1.10.x +For Chamilo 1.10.x, the Videoconference plugin has two new settings options: +*Enable global conference* and *Enable conference in course groups*. + +##### Database changes +You need execute these SQL queries in your database after making the migration process from 1.9.x. + +```sql +ALTER TABLE plugin_bbb_meeting ADD voice_bridge int NOT NULL DEFAULT 1; +ALTER TABLE plugin_bbb_meeting ADD group_id int unsigned NOT NULL DEFAULT 0; +``` +## Migrating to Chamilo LMS 1.11.x +For Chamilo 1.11.x, Videoconference plugin has one new setting option: +*Disable Course Settings*. + +##### Database changes +You need execute this SQL query in your database after making the Chamilo migration process from 1.10.x. +> If you are migrating from 1.9.x versions, you need execute the SQL queries from the migration to 1.10.x before. + +```sql +ALTER TABLE plugin_bbb_meeting ADD user_id int unsigned NOT NULL DEFAULT 0; +ALTER TABLE plugin_bbb_meeting ADD access_url int NOT NULL DEFAULT 1; +``` +For version 2.5 you need execute these SQL queries +```sql +CREATE TABLE IF NOT EXISTS plugin_bbb_room ( + id int NOT NULL AUTO_INCREMENT PRIMARY KEY, + meeting_id int(10) unsigned NOT NULL, + participant_id int(11) NOT NULL, + in_at datetime NOT NULL, + out_at datetime NOT NULL +); +ALTER TABLE plugin_bbb_meeting ADD COLUMN video_url TEXT NULL; +ALTER TABLE plugin_bbb_meeting ADD COLUMN has_video_m4v TINYINT NOT NULL DEFAULT 0; +ALTER TABLE plugin_bbb_meeting ADD COLUMN user_id INT DEFAULT 0; +ALTER TABLE plugin_bbb_meeting ADD COLUMN access_url INT DEFAULT 0; +ALTER TABLE plugin_bbb_meeting ADD COLUMN remote_id char(30); +ALTER TABLE plugin_bbb_meeting ADD COLUMN visibility TINYINT NOT NULL DEFAULT 1; +ALTER TABLE plugin_bbb_meeting ADD COLUMN session_id INT DEFAULT 0; +``` +For version 2.6 (adding limits) you need execute these SQL queries +```sql +INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('bbb_max_users_limit', 'bbb', 'setting', 'Plugins', '3', 'bbb', null, null, null, 1, 1, 0); +INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (2, 15, 'plugin_bbb_course_users_limit', 'MaxUsersInConferenceRoom', '0', 1, 1, 0, 1, null, '2017-05-28 01:19:32'); +INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (3, 15, 'plugin_bbb_session_users_limit', 'MaxUsersInConferenceRoom', null, 1, 1, 0, 1, null, '2017-05-28 01:19:32'); +``` + +For version 2.7 +```sql +ALTER TABLE plugin_bbb_meeting ADD COLUMN interface INT NOT NULL DEFAULT 0; +ALTER TABLE plugin_bbb_room ADD COLUMN interface INT NOT NULL DEFAULT 0; +ALTER TABLE plugin_bbb_room MODIFY COLUMN in_at datetime; +ALTER TABLE plugin_bbb_room MODIFY COLUMN out_at datetime; +``` + +For version 2.8 +```sql +ALTER TABLE plugin_bbb_meeting ADD COLUMN internal_meeting_id VARCHAR(255) DEFAULT NULL; +ALTER TABLE plugin_bbb_room ADD close INT NOT NULL DEFAULT 0; +``` + +For version 2.9 (Optional, requires an update version of BBB) + +```sql +ALTER TABLE plugin_bbb_room DROP COLUMN interface; +ALTER TABLE plugin_bbb_meeting DROP COLUMN interface; +``` + +For version 2.10 (Handles multiple recording formats - Check https://github.com/chamilo/chamilo-lms/issues/3703) + +```sql +CREATE TABLE plugin_bbb_meeting_format ( + id int unsigned not null PRIMARY KEY AUTO_INCREMENT, + meeting_id int unsigned not null, + format_type varchar(255) not null, + resource_url text not null +) +``` + +## Improve access tracking in BBB +You need to configure the cron using the *cron_close_meeting.php* file. + +# Digital ocean VM + +In order to use DigitalOceanVM classes a new package is required: + +``` +composer requires toin0u/digitalocean +``` \ No newline at end of file diff --git a/plugin/bbb/admin.php b/plugin/bbb/admin.php new file mode 100644 index 000000000..d1b3304fe --- /dev/null +++ b/plugin/bbb/admin.php @@ -0,0 +1,166 @@ +get_lang('Videoconference'); +$isGlobal = isset($_GET['global']) ? true : false; + +$bbb = new bbb('', '', $isGlobal); +$action = isset($_GET['action']) ? $_GET['action'] : null; + +$currentMonth = date('n'); +$dateStart = isset($_REQUEST['search_meeting_start']) ? $_REQUEST['search_meeting_start'] : date('Y-m-d', mktime(1, 1, 1, $currentMonth, 1, date('Y'))); +$dateEnd = isset($_REQUEST['search_meeting_end']) ? $_REQUEST['search_meeting_end'] : date('Y-m-d', mktime(1, 1, 1, ++$currentMonth, 0, date('Y'))); + +$dateRange = [ + 'search_meeting_start' => $dateStart, + 'search_meeting_end' => $dateEnd, +]; + +$form = new FormValidator(get_lang('Search')); +$form->addDatePicker('search_meeting_start', get_lang('DateStart')); +$form->addDatePicker('search_meeting_end', get_lang('DateEnd')); +$form->addButtonSearch(get_lang('Search')); +$form->setDefaults($dateRange); + +if ($form->validate()) { + $dateRange = $form->getSubmitValues(); +} + +$meetings = $bbb->getMeetings(0, 0, 0, true, $dateRange); + +foreach ($meetings as &$meeting) { + $participants = $bbb->findConnectedMeetingParticipants($meeting['id']); + foreach ($participants as $meetingParticipant) { + /** @var User $participant */ + $participant = $meetingParticipant['participant']; + if ($participant) { + $meeting['participants'][] = $participant->getCompleteName().' ('.$participant->getEmail().')'; + } + } +} + +if ($action) { + switch ($action) { + case 'export': + $dataToExport = [ + [$tool_name, $plugin->get_lang('RecordList')], + [], + [ + get_lang('CreatedAt'), + get_lang('Status'), + $plugin->get_lang('Records'), + get_lang('Course'), + get_lang('Session'), + get_lang('Participants'), + ], + ]; + + foreach ($meetings as $meeting) { + $dataToExport[] = [ + $meeting['created_at'], + $meeting['status'] == 1 ? $plugin->get_lang('MeetingOpened') : $plugin->get_lang('MeetingClosed'), + $meeting['record'] == 1 ? get_lang('Yes') : get_lang('No'), + $meeting['course'] ? $meeting['course']->getTitle() : '-', + $meeting['session'] ? $meeting['session']->getName() : '-', + isset($meeting['participants']) ? implode(PHP_EOL, $meeting['participants']) : null, + ]; + } + + Export::arrayToXls($dataToExport); + break; + } +} + +if (!empty($meetings)) { + $meetings = array_reverse($meetings); +} + +if (!$bbb->isServerRunning()) { + Display::addFlash( + Display::return_message(get_lang('ServerIsNotRunning'), 'error') + ); +} + +$htmlHeadXtra[] = api_get_js_simple( + api_get_path(WEB_PLUGIN_PATH).'bbb/resources/utils.js' +); +$htmlHeadXtra[] = ""; + +$tpl = new Template($tool_name); +$tpl->assign('meetings', $meetings); +$tpl->assign('search_form', $form->returnForm()); + +$settingsForm = new FormValidator('settings', api_get_self()); +$settingsForm->addHeader($plugin->get_lang('UpdateAllCourseSettings')); +$settingsForm->addHtml(Display::return_message($plugin->get_lang('ThisWillUpdateAllSettingsInAllCourses'))); +$settings = $plugin->course_settings; +$defaults = []; +foreach ($settings as $setting) { + $setting = $setting['name']; + $text = $settingsForm->addText($setting, $plugin->get_lang($setting), false); + $text->freeze(); + $defaults[$setting] = api_get_plugin_setting('bbb', $setting) === 'true' ? get_lang('Yes') : get_lang('No'); +} + +$settingsForm->addButtonSave($plugin->get_lang('UpdateAllCourses')); + +if ($settingsForm->validate()) { + $table = Database::get_course_table(TABLE_COURSE_SETTING); + foreach ($settings as $setting) { + $setting = $setting['name']; + $setting = Database::escape_string($setting); + + if (empty($setting)) { + continue; + } + $value = api_get_plugin_setting('bbb', $setting); + if ($value === 'true') { + $value = 1; + } else { + $value = ''; + } + $sql = "UPDATE $table SET value = '$value' WHERE variable = '$setting'"; + Database::query($sql); + } + Display::addFlash(Display::return_message(get_lang('Updated'))); + header('Location: '.api_get_self()); + exit; +} + +$settingsForm->setDefaults($defaults); +$tpl->assign('settings_form', $settingsForm->returnForm()); +$content = $tpl->fetch('bbb/view/admin.tpl'); +if ($meetings) { + $actions = Display::toolbarButton( + get_lang('ExportInExcel'), + api_get_self().'?'.http_build_query([ + 'action' => 'export', + 'search_meeting_start' => $dateStart, + 'search_meeting_end' => $dateEnd, + ]), + 'file-excel-o', + 'success' + ); + + $tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actions]) + ); +} + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/bbb/ajax.php b/plugin/bbb/ajax.php new file mode 100644 index 000000000..fa92c5a94 --- /dev/null +++ b/plugin/bbb/ajax.php @@ -0,0 +1,47 @@ +checkDirectMeetingVideoUrl($meetingId)) { + $meetingInfo = Database::select( + '*', + 'plugin_bbb_meeting', + ['where' => ['id = ?' => intval($meetingId)]], + 'first' + ); + + $url = $meetingInfo['video_url'].'/capture.m4v'; + $link = Display::url( + Display::return_icon('save.png', get_lang('DownloadFile')), + $meetingInfo['video_url'].'/capture.m4v', + ['target' => '_blank'] + ); + + header('Content-Type: application/json'); + echo json_encode(['url' => $url, 'link' => $link]); + } + break; +} diff --git a/plugin/bbb/config.php b/plugin/bbb/config.php new file mode 100644 index 000000000..325f9b9d3 --- /dev/null +++ b/plugin/bbb/config.php @@ -0,0 +1,6 @@ + true, + 'vms' => [ + [ + 'enabled' => true, + 'name' => 'DigitalOcean', + 'vm_client_id' => 'client_id', + 'api_key' => '123456', + 'vm_id' => '123456', // The VM ID we want to access + 'vm_min_size_id' => '66', // VM size ID example for 512mb use 66 + 'vm_max_size_id' => '65', // For 1GB use 63 + ], + // The Amazon hook is not implemented yet + [ + 'enabled' => false, + 'name' => 'Amazon', + ], + ], +]; diff --git a/plugin/bbb/course_index.php b/plugin/bbb/course_index.php new file mode 100644 index 000000000..c03e274ff --- /dev/null +++ b/plugin/bbb/course_index.php @@ -0,0 +1,11 @@ +isEnabled()) { + $bbb = new bbb(); + if ($bbb->pluginEnabled) { + $activeSessions = $bbb->getActiveSessionsCount(); + if (empty($activeSessions)) { + $vm->runCron(); + } else { + echo "Can't run cron active sessions found: ".$activeSessions; + } + } + } +} diff --git a/plugin/bbb/cron_close_meeting.php b/plugin/bbb/cron_close_meeting.php new file mode 100644 index 000000000..030841660 --- /dev/null +++ b/plugin/bbb/cron_close_meeting.php @@ -0,0 +1,113 @@ +get('plugin_bbb_multiple_urls_cron_apply_to_all'); + +$bbb = new bbb(); + +if (!$bbb->pluginEnabled) { + return; +} + +$activeSessions = $bbb->getActiveSessions($applyAllUrls); + +if (empty($activeSessions)) { + return; +} + +foreach ($activeSessions as $value) { + $meetingId = $value['id']; + $courseCode = null; + $courseInfo = api_get_course_info_by_id($value['c_id']); + if (!empty($courseInfo)) { + $courseCode = $courseInfo['code']; + } + + $meetingBBB = $bbb->getMeetingInfo( + [ + 'meetingId' => $value['remote_id'], + 'password' => $value['moderator_pw'], + ] + ); + + if ($meetingBBB === false) { + //checking with the remote_id didn't work, so just in case and + // to provide backwards support, check with the id + $params = [ + 'meetingId' => $value['id'], + 'password' => $value['moderator_pw'], + ]; + $meetingBBB = $bbb->getMeetingInfo($params); + } + + if (empty($meetingBBB)) { + continue; + } + + if (!isset($meetingBBB['returncode'])) { + continue; + } + + $action = (string) $meetingBBB['returncode']; + + switch ($action) { + case 'FAILED': + $bbb->endMeeting($value['id'], $courseCode); + break; + case 'SUCCESS': + Database::update( + $roomTable, + ['close' => BBBPlugin::ROOM_CHECK], + ['meeting_id = ? AND close= ?' => [$meetingId, BBBPlugin::ROOM_OPEN]] + ); + + $i = 0; + while ($i < $meetingBBB['participantCount']) { + $participantId = $meetingBBB[$i]['userId']; + $roomData = Database::select( + '*', + $roomTable, + [ + 'where' => [ + 'meeting_id = ? AND participant_id = ? AND close = ?' => [ + $meetingId, + $participantId, + BBBPlugin::ROOM_CHECK, + ], + ], + 'order' => 'id DESC', + ], + 'first' + ); + + if (!empty($roomData)) { + $roomId = $roomData['id']; + if (!empty($roomId)) { + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_OPEN], + ['id = ? ' => $roomId] + ); + } + } + $i++; + } + + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE], + ['meeting_id = ? AND close= ?' => [$meetingId, BBBPlugin::ROOM_CHECK]] + ); + + break; + } +} diff --git a/plugin/bbb/index.php b/plugin/bbb/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/bbb/index.php @@ -0,0 +1 @@ +install(); diff --git a/plugin/bbb/lang/english.php b/plugin/bbb/lang/english.php new file mode 100644 index 000000000..26b532762 --- /dev/null +++ b/plugin/bbb/lang/english.php @@ -0,0 +1,78 @@ +set one up or ask the Chamilo official providers for a quote. BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.
"; + +$strings['big_blue_button_welcome_message'] = 'Welcome message'; +$strings['enable_global_conference'] = 'Enable global conference'; +$strings['enable_global_conference_per_user'] = 'Enable global conference per user'; +$strings['enable_conference_in_course_groups'] = 'Enable conference in course groups'; +$strings['enable_global_conference_link'] = 'Enable the link to the global conference in the homepage'; +$strings['disable_download_conference_link'] = 'Disable download conference'; +$strings['big_blue_button_record_and_store'] = 'Record and store sessions'; +$strings['bbb_enable_conference_in_groups'] = 'Allow conference in groups'; +$strings['plugin_tool_bbb'] = 'Video'; +$strings['ThereAreNotRecordingsForTheMeetings'] = 'There are not recording for the meeting sessions'; +$strings['NoRecording'] = 'No recording'; +$strings['ClickToContinue'] = 'Click to continue'; +$strings['NoGroup'] = 'No group'; +$strings['UrlMeetingToShare'] = 'URL to share'; +$strings['AdminView'] = 'View for administrators'; +$strings['max_users_limit'] = 'Max users limit'; +$strings['max_users_limit_help'] = 'Set this to the maximum number of users you want to allow by course or session-course. Leave empty or set to 0 to disable this limit.'; +$strings['MaxXUsersWarning'] = 'This conference room has a maximum number of %s simultaneous users.'; +$strings['MaxXUsersReached'] = 'The limit of %s simultaneous users has been reached for this conference room. Please wait for one seat to be freed or for another conference to start in order to join.'; +$strings['MaxXUsersReachedManager'] = 'The limit of %s simultaneous users has been reached for this conference room. To increase this limit, please contact the platform administrator.'; +$strings['MaxUsersInConferenceRoom'] = 'Max simultaneous users in a conference room'; +$strings['global_conference_allow_roles'] = "Global conference link only visible for these user roles"; +$strings['CreatedAt'] = 'Created at'; +$strings['allow_regenerate_recording'] = 'Allow regenerate recording'; +$strings['bbb_force_record_generation'] = 'Force record generation at the end of the meeting'; +$strings['disable_course_settings'] = 'Disable course settings'; +$strings['UpdateAllCourses'] = 'Update all courses'; +$strings['UpdateAllCourseSettings'] = 'Update all course settings'; +$strings['ThisWillUpdateAllSettingsInAllCourses'] = 'This will update at once all your course settings.'; +$strings['ThereIsNoVideoConferenceActive'] = 'There is no videoconference currently active'; +$strings['RoomClosed'] = 'Room closed'; +$strings['RoomClosedComment'] = ' '; +$strings['meeting_duration'] = 'Meeting duration (in minutes)'; +$strings['big_blue_button_students_start_conference_in_groups'] = 'Allow students to start conference in their groups.'; +$strings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'Automatically closes all rooms on ALL campuses.'; +$strings['plugin_bbb_multiple_urls_cron_apply_to_all_help'] = 'Option for multi-url environments. Allows the CRON task to close all open rooms on mother and child campus.'; diff --git a/plugin/bbb/lang/french.php b/plugin/bbb/lang/french.php new file mode 100644 index 000000000..5db2266c2 --- /dev/null +++ b/plugin/bbb/lang/french.php @@ -0,0 +1,71 @@ +en installer un avant de poursuivre, ou demander un devis à l'un des fournisseurs officiels de Chamilo. BigBlueButton est un outil de logiciel libre (et gratuit), mais son installation pourrait présenter une certaine complexité et demander des compétences qui ne sont peut-être pas à la portée de tous. Vous pouvez l'installer vous-même à partir de la documentation (disponible publiquement) de BigBlueButton, ou recherchez un soutien professionnel. Ce soutien pourrait générer certains coûts (au moins le temps de la personne qui vous assiste dans l'opération). Dans le plus pur esprit du logiciel libre, nous vous fournissons les outils pour simplifier votre travail dans la mesure de nos possibilités, et nous vous recommandons des professionnels (les fournisseurs officiels de Chamilo) pour vous venir en aide au cas où ceux-ci seraient insuffisants.
"; + +$strings['big_blue_button_welcome_message'] = 'Message de bienvenue de BigBlueButton'; +$strings['enable_global_conference'] = 'Activer les conférences globales'; +$strings['enable_global_conference_per_user'] = 'Activer les conférences globales par utilisateur'; +$strings['enable_conference_in_course_groups'] = 'Activer les conférences dans les groupes'; +$strings['enable_global_conference_link'] = 'Activer le lien vers la conférence globale sur la page principale'; + +$strings['big_blue_button_record_and_store'] = 'Enregistrer les sessions de vidéoconférence'; +$strings['bbb_enable_conference_in_groups'] = 'Permettre la création de vidéoconférence pour les groupes'; +$strings['plugin_tool_bbb'] = 'Vidéo'; +$strings['ThereAreNotRecordingsForTheMeetings'] = 'Aucun enregistrement disponible'; +$strings['NoRecording'] = "Pas d'enregistrement"; +$strings['ClickToContinue'] = 'Cliquez pour continuer'; +$strings['NoGroup'] = 'Sans groupe'; +$strings['UrlMeetingToShare'] = 'URL à partager'; + +$strings['AdminView'] = 'Vue administrateur'; +$strings['max_users_limit'] = 'Utilisateurs maximum'; +$strings['max_users_limit_help'] = 'Nombre maximum d\'utilisateurs simultanés dans une salle de vidéoconférence de cours ou cours-session. Laisser vide ou sur 0 pour ne pas assigner de limite.'; +$strings['MaxXUsersWarning'] = 'Cette salle de conférence est limitée à %s utilisateurs simultanés.'; +$strings['MaxXUsersReached'] = 'La limite de %s utilisateurs simultanés a été atteinte dans cette salle de conférence. Veuillez rafraîchir dans quelque minutes pour voir si un siège s\'est libéré, ou attendre l\'ouverture d\'une nouvelle salle de conférence pour participer.'; +$strings['MaxXUsersReachedManager'] = 'La limite de %s utilisateurs simultanés a été atteinte dans cette salle de conférence. Pour augmenter la limite, prenez contact avec l\'administrateur du portail.'; +$strings['MaxUsersInConferenceRoom'] = 'Nombre max d\'utilisateurs simultanés dans une salle de conférence'; +$strings['global_conference_allow_roles'] = "Visibilité du lien de vidéo conférence global pour les profils suivant"; +$strings['CreatedAt'] = "Créé à"; + +$strings['bbb_force_record_generation'] = 'Forcer la génération de l\'enregistrement à la fin de la session'; +$strings['ThereIsNoVideoConferenceActive'] = "Il n'y a aucune vidéoconférence actuellement active"; +$strings['meeting_duration'] = 'Durée de la conférence (en minutes)'; +$strings['big_blue_button_students_start_conference_in_groups'] = 'Permettre aux apprenants de démarrer les vidéoconferénces de leurs groupes'; diff --git a/plugin/bbb/lang/german.php b/plugin/bbb/lang/german.php new file mode 100644 index 000000000..f670f321e --- /dev/null +++ b/plugin/bbb/lang/german.php @@ -0,0 +1,65 @@ +set one up or ask the Chamilo official providers for a quote. BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.
"; + +$strings['big_blue_button_welcome_message'] = 'Willkommensnachricht'; +$strings['enable_global_conference'] = 'Globale Konferenz aktivieren'; +$strings['enable_global_conference_per_user'] = 'Globale Konferenz pro Benutzer aktivieren'; +$strings['enable_conference_in_course_groups'] = 'Konferenz in Kurs-Gruppen aktivieren'; +$strings['enable_global_conference_link'] = 'Aktivieren Sie den Link zur globalen Konferenz auf der Website'; + +$strings['big_blue_button_record_and_store'] = 'Aufnehmen und Speichern von Meetings'; +$strings['bbb_enable_conference_in_groups'] = 'Konferenz in Gruppen zulassen'; +$strings['plugin_tool_bbb'] = 'Video'; +$strings['ThereAreNotRecordingsForTheMeetings'] = 'Es gibt keine Aufnahmen von den Meetings'; +$strings['NoRecording'] = 'Keine Videoaufzeichnung'; +$strings['ClickToContinue'] = 'Klicken um fortzufahren'; +$strings['NoGroup'] = 'Keine Gruppe'; +$strings['UrlMeetingToShare'] = 'URL zu teilen'; + +$strings['AdminView'] = 'Administrator Ansicht'; +$strings['max_users_limit'] = 'Maximale Anzahl von Benutzern'; +$strings['max_users_limit_help'] = 'Setzen Sie diese auf die maximale Anzahl der Benutzer, die Sie nach Kurs oder Session-Kurs erlauben möchten. Leer lassen oder auf 0 setzen, um dieses Limit zu deaktivieren.'; +$strings['MaxXUsersWarning'] = 'Dieser Konferenzraum hat eine maximale Anzahl von %s gleichzeitigen Benutzern.'; +$strings['MaxXUsersReached'] = 'The limit of %s simultaneous users has been reached for this conference room. Please wait for one seat to be freed or for another conference to start in order to join.'; +$strings['MaxXUsersReachedManager'] = 'The limit of %s simultaneous users has been reached for this conference room. To increase this limit, please contact the platform administrator.'; +$strings['MaxUsersInConferenceRoom'] = 'Max simultaneous users in a conference room'; +$strings['global_conference_allow_roles'] = "Globaler Konferenz-Link nur für diese Benutzerrollen sichtbar"; +$strings['CreatedAt'] = "Erstellt am"; diff --git a/plugin/bbb/lang/spanish.php b/plugin/bbb/lang/spanish.php new file mode 100644 index 000000000..6be449b6d --- /dev/null +++ b/plugin/bbb/lang/spanish.php @@ -0,0 +1,71 @@ +configure uno antes de seguir, o pida una cotización a uno de los proveedores oficiales de Chamilo. BigBlueButton es una herramienta de software libre (y gratuita), pero su instalación requiere de competencias que quizás no sean inmediatamente disponibles para todos. Puede instalarla usted mismo o buscar ayuda profesional. Esta ayuda podría no obstante generar algunos costos (por lo menos el tiempo de la persona quien lo ayude). En el puro espíritu del software libre, le ofrecemos las herramientas para hacer su trabajo más simple, en la medida de nuestras posibilidades, y le recomendamos profesionales (los proveedores oficiales de Chamilo) para ayudarlo en caso esto fuera demasiado complicado.
"; + +$strings['big_blue_button_welcome_message'] = 'Mensaje de bienvenida de BigBlueButton'; +$strings['enable_global_conference'] = 'Activar la conferencia global'; +$strings['enable_global_conference_per_user'] = 'Activar la conferencia global por usuario'; +$strings['enable_conference_in_course_groups'] = 'Activar las conferencias en grupos'; +$strings['enable_global_conference_link'] = 'Activar el enlace hacia la conferencia global desde la página principal'; + +$strings['big_blue_button_record_and_store'] = 'Grabar las sesiones de videoconferencia.'; +$strings['bbb_enable_conference_in_groups'] = 'Activar la creación de videoconferencia en los grupos.'; +$strings['plugin_tool_bbb'] = 'Videoconferencia'; +$strings['ThereAreNotRecordingsForTheMeetings'] = 'No hay grabaciones de sesiones de videoconferencia'; +$strings['NoRecording'] = 'No hay grabación'; +$strings['ClickToContinue'] = 'Hacer click para continuar'; +$strings['NoGroup'] = 'No hay grupo'; +$strings['UrlMeetingToShare'] = 'URL a compartir'; + +$strings['AdminView'] = 'Vista para administradores'; +$strings['max_users_limit'] = 'Cantidad máxima de usuarios'; +$strings['max_users_limit_help'] = 'Este valor indica la cantidad máxima de usuarios simultáneos en una conferencia en un curso o curso-sesión. Dejar vacío o en 0 para no poner límite.'; +$strings['MaxXUsersWarning'] = 'Esta sala de conferencia es limitada a un máximo de %s usuarios simultáneos.'; +$strings['MaxXUsersReached'] = 'El límite de %s usuarios simultáneos ha sido alcanzado en esta sala de conferencia. Por favor refresque la página en unos minutos para ver si un asiento se ha liberado, o espere la apertura de una nueva sala para poder participar.'; +$strings['MaxXUsersReachedManager'] = 'El límite de %s usuarios simultáneos ha sido alcanzado en esta sala de conferencia. Para aumentar el límite, contáctese con el administrador del portal.'; +$strings['MaxUsersInConferenceRoom'] = 'Número máximo de usuarios simultáneos en una sala de conferencia'; +$strings['global_conference_allow_roles'] = 'El enlace de videoconferencia global es disponible para estos perfiles'; +$strings['CreatedAt'] = 'Creado el'; +$strings['ThereIsNoVideoConferenceActive'] = "No hay una videoconferencia actualmente activa"; +$strings['meeting_duration'] = 'Duración de la reunión (en minutos)'; +$strings['big_blue_button_students_start_conference_in_groups'] = 'Permitir a los estudiantes iniciar una videoconferencia en sus grupos.'; +$strings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'Cerrar automáticamente todas las salas sin actividad en TODOS los campus.'; +$strings['plugin_bbb_multiple_urls_cron_apply_to_all_help'] = 'Opción para entornos multi-url. Permite a la tarea CRON cerrar todas las salas abiertas del campus madre e hijos.'; diff --git a/plugin/bbb/lib/VM.php b/plugin/bbb/lib/VM.php new file mode 100644 index 000000000..591371797 --- /dev/null +++ b/plugin/bbb/lib/VM.php @@ -0,0 +1,116 @@ +config = $config; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled + * @return bool + */ + public function isEnabled(bool $checkEnabled = false): bool + { + $config = $this->getConfig(); + + if (!isset($config)) { + + return false; + } + + if (!is_array($config)) { + return false; + } + + if (isset($config['enabled']) && $config['enabled']) { + + return true; + } + + return false; + } + + /** + * @return VirtualMachineInterface + */ + public function getVirtualMachine() + { + return $this->virtualMachine; + } + + /** + * @param VirtualMachineInterface $virtualMachine + */ + public function setVirtualMachine(VirtualMachineInterface $virtualMachine) + { + $this->virtualMachine = $virtualMachine; + } + + /** + * @return VirtualMachineInterface + */ + public function getVirtualMachineFromConfig() + { + $vmList = $this->config['vms']; + + foreach ($vmList as $vm) { + if (isset($vm['enabled']) && $vm['enabled'] == true) { + $className = $vm['name'].'VM'; + + return new $className($vm); + break; + } + } + + return false; + } + + /** + * Resize the VM to the max size + */ + public function resizeToMaxLimit() + { + $virtualMachine = $this->getVirtualMachineFromConfig(); + $this->setVirtualMachine($virtualMachine); + $virtualMachine->resizeToMaxLimit(); + } + + /** + * Resize the VM to the min size + */ + public function resizeToMinLimit() + { + $virtualMachine = $this->getVirtualMachineFromConfig(); + $this->setVirtualMachine($virtualMachine); + $virtualMachine->resizeToMinLimit(); + } + + public function runCron() + { + $virtualMachine = $this->getVirtualMachineFromConfig(); + $this->setVirtualMachine($virtualMachine); + + $virtualMachine->runCron(); + } +} diff --git a/plugin/bbb/lib/bbb.lib.php b/plugin/bbb/lib/bbb.lib.php new file mode 100644 index 000000000..904774596 --- /dev/null +++ b/plugin/bbb/lib/bbb.lib.php @@ -0,0 +1,2232 @@ +courseCode = api_get_course_id(); + $this->courseId = api_get_course_int_id(); + $this->sessionId = api_get_session_id(); + $this->groupId = api_get_group_id(); + + // Initialize video server settings from global settings + $this->plugin = BBBPlugin::create(); + $bbbPluginEnabled = $this->plugin->get('tool_enable'); + + $bbb_host = !empty($host) ? $host : $this->plugin->get('host'); + $bbb_salt = !empty($salt) ? $salt : $this->plugin->get('salt'); + + $this->table = Database::get_main_table('plugin_bbb_meeting'); + $this->enableGlobalConference = $this->plugin->get('enable_global_conference') === 'true'; + $this->isGlobalConference = (bool) $isGlobalConference; + + $columns = Database::listTableColumns($this->table); + $this->groupSupport = isset($columns['group_id']) ? true : false; + $this->userSupport = isset($columns['user_id']) ? true : false; + $this->accessUrl = api_get_current_access_url_id(); + + $this->enableGlobalConferencePerUser = false; + if ($this->userSupport && !empty($isGlobalPerUser)) { + $this->enableGlobalConferencePerUser = $this->plugin->get('enable_global_conference_per_user') === 'true'; + $this->userId = $isGlobalPerUser; + } + + if ($this->groupSupport) { + // Plugin check + $this->groupSupport = $this->plugin->get('enable_conference_in_course_groups') === 'true' ? true : false; + if ($this->groupSupport) { + // Platform check + $bbbSetting = api_get_setting('bbb_enable_conference_in_course_groups'); + $bbbSetting = isset($bbbSetting['bbb']) ? $bbbSetting['bbb'] === 'true' : false; + + if ($bbbSetting) { + // Course check + $courseInfo = api_get_course_info(); + if ($courseInfo) { + $this->groupSupport = api_get_course_plugin_setting( + 'bbb', + 'bbb_enable_conference_in_groups', + $courseInfo + ) === '1'; + } + } + } + } + $this->maxUsersLimit = $this->plugin->get('max_users_limit'); + + if ($bbbPluginEnabled === 'true') { + $userInfo = api_get_user_info(); + if (empty($userInfo) && !empty($isGlobalPerUser)) { + // If we are following a link to a global "per user" conference + // then generate a random guest name to join the conference + // because there is no part of the process where we give a name + //$this->userCompleteName = 'Guest'.rand(1000, 9999); + } else { + $this->userCompleteName = $userInfo['complete_name']; + } + + if (api_is_anonymous()) { + $this->userCompleteName = get_lang('Guest').'_'.rand(1000, 9999); + } + + $this->salt = $bbb_salt; + if (!empty($bbb_host)) { + if (substr($bbb_host, -1, 1) !== '/') { + $bbb_host .= '/'; + } + $this->url = $bbb_host; + if (!preg_match('#/bigbluebutton/$#', $bbb_host)) { + $this->url = $bbb_host.'bigbluebutton/'; + } + } + $info = parse_url($bbb_host); + + if (isset($info['scheme'])) { + $this->protocol = $info['scheme'].'://'; + $this->url = str_replace($this->protocol, '', $this->url); + $urlWithProtocol = $bbb_host; + } else { + // We assume it's an http, if user wants to use https, the host *must* include the protocol. + $this->protocol = 'http://'; + $urlWithProtocol = $this->protocol.$bbb_host; + } + + // Setting BBB api + define('CONFIG_SECURITY_SALT', $this->salt); + define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol); + define('CONFIG_SERVER_BASE_URL', $this->url); + define('CONFIG_SERVER_PROTOCOL', $this->protocol); + + $this->api = new BigBlueButtonBN(); + $this->pluginEnabled = true; + $this->logoutUrl = $this->getListingUrl(); + } + } + + /** + * @param int $courseId Optional. Course ID. + * @param int $sessionId Optional. Session ID. + * @param int $groupId Optional. Group ID. + * + * @return string + */ + public function getListingUrl($courseId = 0, $sessionId = 0, $groupId = 0) + { + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?' + .$this->getUrlParams($courseId, $sessionId, $groupId); + } + + /** + * @param int $courseId Optional. Course ID. + * @param int $sessionId Optional. Session ID. + * @param int $groupId Optional. Group ID. + * + * @return string + */ + public function getUrlParams($courseId = 0, $sessionId = 0, $groupId = 0) + { + if (empty($this->courseCode) && !$courseId) { + if ($this->isGlobalConferencePerUserEnabled()) { + return 'global=1&user_id='.$this->userId; + } + + if ($this->isGlobalConference()) { + return 'global=1'; + } + + return ''; + } + + $courseCode = $this->courseCode; + if (!empty($courseId)) { + $course = api_get_course_info_by_id($courseId); + if ($course) { + $courseCode = $course['code']; + } + } + + return http_build_query( + [ + 'cidReq' => $courseCode, + 'id_session' => $sessionId ?: $this->sessionId, + 'gidReq' => $groupId ?: $this->groupId, + ] + ); + } + + /** + * @return bool + */ + public function isGlobalConferencePerUserEnabled() + { + return $this->enableGlobalConferencePerUser; + } + + /** + * @return bool + */ + public function isGlobalConference() + { + if ($this->isGlobalConferenceEnabled() === false) { + return false; + } + + return (bool) $this->isGlobalConference; + } + + /** + * @return bool + */ + public function isGlobalConferenceEnabled() + { + return $this->enableGlobalConference; + } + + /** + * @param array $userInfo + * + * @return bool + */ + public static function showGlobalConferenceLink($userInfo) + { + if (empty($userInfo)) { + return false; + } + $setting = api_get_plugin_setting('bbb', 'enable_global_conference'); + $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link'); + if ($setting === 'true' && $settingLink === 'true') { + //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url); + $allowedRoles = api_get_plugin_setting( + 'bbb', + 'global_conference_allow_roles' + ); + + if (api_is_platform_admin()) { + $userInfo['status'] = PLATFORM_ADMIN; + } + + $showGlobalLink = true; + if (!empty($allowedRoles)) { + if (!in_array($userInfo['status'], $allowedRoles)) { + $showGlobalLink = false; + } + } + + return $showGlobalLink; + } + } + + /** + * Gets the global limit of users in a video-conference room. + * This value can be overridden by course-specific values + * @return int Maximum number of users set globally + */ + public function getMaxUsersLimit() + { + $limit = $this->maxUsersLimit; + if ($limit <= 0) { + $limit = 0; + } + $courseLimit = 0; + $sessionLimit = 0; + // Check the extra fields for this course and session + // Session limit takes priority over course limit + // Course limit takes priority over global limit + if (!empty($this->courseId)) { + $extraField = new ExtraField('course'); + $fieldId = $extraField->get_all( + array('variable = ?' => 'plugin_bbb_course_users_limit') + ); + $extraValue = new ExtraFieldValue('course'); + $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId[0]['id']); + if (!empty($value['value'])) { + $courseLimit = (int) $value['value']; + } + } + if (!empty($this->sessionId)) { + $extraField = new ExtraField('session'); + $fieldId = $extraField->get_all( + array('variable = ?' => 'plugin_bbb_session_users_limit') + ); + $extraValue = new ExtraFieldValue('session'); + $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId[0]['id']); + if (!empty($value['value'])) { + $sessionLimit = (int) $value['value']; + } + } + + if (!empty($sessionLimit)) { + return $sessionLimit; + } elseif (!empty($courseLimit)) { + return $courseLimit; + } + + return (int) $limit; + } + + /** + * Sets the global limit of users in a video-conference room. + * + * @param int Maximum number of users (globally) + */ + public function setMaxUsersLimit($max) + { + if ($max < 0) { + $max = 0; + } + $this->maxUsersLimit = (int) $max; + } + + /** + * See this file in you BBB to set up default values + * + * @param array $params Array of parameters that will be completed if not containing all expected variables + * + * /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties + * + * More record information: + * http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification + * + * Default maximum number of users a meeting can have. + * Doesn't get enforced yet but is the default value when the create + * API doesn't pass a value. + * defaultMaxUsers=20 + * + * Default duration of the meeting in minutes. + * Current default is 0 (meeting doesn't end). + * defaultMeetingDuration=0 + * + * Remove the meeting from memory when the end API is called. + * This allows 3rd-party apps to recycle the meeting right-away + * instead of waiting for the meeting to expire (see below). + * removeMeetingWhenEnded=false + * + * The number of minutes before the system removes the meeting from memory. + * defaultMeetingExpireDuration=1 + * + * The number of minutes the system waits when a meeting is created and when + * a user joins. If after this period, a user hasn't joined, the meeting is + * removed from memory. + * defaultMeetingCreateJoinDuration=5 + * + * @return mixed + */ + public function createMeeting($params) + { + $params['c_id'] = api_get_course_int_id(); + $params['session_id'] = api_get_session_id(); + + if ($this->hasGroupSupport()) { + $params['group_id'] = api_get_group_id(); + } + + if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) { + $params['user_id'] = (int) $this->userId; + } + + $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword(); + $attendeePassword = $params['attendee_pw']; + $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword(); + $moderatorPassword = $params['moderator_pw']; + + $params['record'] = api_get_course_plugin_setting('bbb', 'big_blue_button_record_and_store') == 1; + $max = api_get_course_plugin_setting('bbb', 'max_users_limit'); + $max = isset($max) ? $max : -1; + + $params['status'] = 1; + // Generate a pseudo-global-unique-id to avoid clash of conferences on + // the same BBB server with several Chamilo portals + $params['remote_id'] = uniqid(true, true); + // Each simultaneous conference room needs to have a different + // voice_bridge composed of a 5 digits number, so generating a random one + $params['voice_bridge'] = rand(10000, 99999); + $params['created_at'] = api_get_utc_datetime(); + $params['access_url'] = $this->accessUrl; + + $id = Database::insert($this->table, $params); + + if ($id) { + Event::addEvent( + 'bbb_create_meeting', + 'meeting_id', + (int) $id, + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + + $meetingName = $params['meeting_name'] ?? $this->generateVideoConferenceName(); + $welcomeMessage = $params['welcome_msg'] ?? null; + $record = $params['record'] ? 'true' : 'false'; + //$duration = isset($params['duration']) ? intval($params['duration']) : 0; + // This setting currently limits the maximum conference duration, + // to avoid lingering sessions on the video-conference server #6261 + $duration = 300; + $meetingDuration = (int) $this->plugin->get('meeting_duration'); + if (!empty($meetingDuration)) { + $duration = $meetingDuration; + } + $url = api_get_access_url(api_get_current_access_url_id())['url']; + $bbbParams = array( + 'meetingId' => $params['remote_id'], // REQUIRED + 'meetingName' => $meetingName, // REQUIRED + 'attendeePw' => $attendeePassword, // Match this value in getJoinMeetingURL() to join as attendee. + 'moderatorPw' => $moderatorPassword, // Match this value in getJoinMeetingURL() to join as moderator. + 'welcomeMsg' => $welcomeMessage, // ''= use default. Change to customize. + 'dialNumber' => '', // The main number to call into. Optional. + 'voiceBridge' => $params['voice_bridge'], // PIN to join voice. Required. + 'webVoice' => '', // Alphanumeric to join voice. Optional. + 'logoutUrl' => $this->logoutUrl.'&action=logout&remote_id='.$params['remote_id'], + 'maxParticipants' => $max, // Optional. -1 = unlimitted. Not supported in BBB. [number] + 'record' => $record, // New. 'true' will tell BBB to record the meeting. + 'duration' => $duration, // Default = 0 which means no set duration in minutes. [number] + 'meta_OriginURL' => $url, // Add url information to BBB meeting info (see 'meta' info at https://docs.bigbluebutton.org/dev/api.html#create) + //'meta_category' => '', // Use to pass additional info to BBB server. See API docs. + ); + + $status = false; + $meeting = null; + while ($status === false) { + $result = $this->api->createMeetingWithXmlResponseArray($bbbParams); + if (isset($result) && strval($result['returncode']) == 'SUCCESS') { + if ($this->plugin->get('allow_regenerate_recording') === 'true') { + $internalId = Database::escape_string($result['internalMeetingID']); + $sql = "UPDATE $this->table SET internal_meeting_id = '".$internalId."' + WHERE id = $id"; + Database::query($sql); + } + $meeting = $this->joinMeeting($meetingName, true); + + return $meeting; + } + } + } + + return false; + } + + /** + * @return bool + */ + public function hasGroupSupport() + { + return $this->groupSupport; + } + + /** + * Gets the password for a specific meeting for the current user + * + * @param string $courseCode + * + * @return string A moderator password if user is teacher, or the course code otherwise + * + */ + public function getUserMeetingPassword($courseCode = null) + { + if ($this->isGlobalConferencePerUserEnabled()) { + return 'url_'.$this->userId.'_'.api_get_current_access_url_id(); + } + + if ($this->isGlobalConference()) { + return 'url_'.api_get_current_access_url_id(); + } + $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode; + + return $courseCode; + } + + /** + * Generated a moderator password for the meeting. + * + * @param string $courseCode + * + * @return string A password for the moderation of the videoconference + */ + public function getModMeetingPassword($courseCode = null) + { + if ($this->isGlobalConferencePerUserEnabled()) { + return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod'; + } + + if ($this->isGlobalConference()) { + return 'url_'.api_get_current_access_url_id().'_mod'; + } + + $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode; + + return $courseCode.'mod'; + } + + /** + * Get the info from the current open videoconference. + * Otherwise, return false. + * + * @return array|bool + */ + public function getCurrentVideoConference() + { + $whereConditions = [ + 'status = ?' => 1, + ]; + + if ($this->isGlobalConferencePerUserEnabled()) { + $whereConditions[' AND user_id = ?'] = $this->userId; + } + + if ($this->isGlobalConference()) { + $whereConditions[' AND access_url = ?'] = api_get_current_access_url_id(); + } + + if ($this->hasGroupSupport()) { + $whereConditions[' AND group_id = ?'] = api_get_group_id(); + } + + $cId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + + if ($cId) { + $whereConditions[' AND c_id = ?'] = api_get_course_int_id(); + } + + if ($sessionId) { + $whereConditions[' AND session_id = ?'] = api_get_session_id(); + } + + return Database::select( + '*', + $this->table, + [ + 'where' => $whereConditions, + 'order' => 'created_at DESC', + ], + 'first' + ); + } + + public function generateVideoConferenceName(string $defaultName = null): string + { + $nameFilter = function ($name) { + return URLify::filter( + $name, + 64, + '', + true, + true, + true, + false + ); + }; + + if (!empty($defaultName)) { + $name = $nameFilter($defaultName); + + if (!empty($name)) { + return $name; + } + } + + $urlId = api_get_current_access_url_id(); + + if ($this->isGlobalConferencePerUserEnabled()) { + return $nameFilter("url_{$this->userId}_$urlId"); + } + + if ($this->isGlobalConference()) { + return $nameFilter("url_$urlId"); + } + + $course = api_get_course_entity(); + $session = api_get_session_entity(); + $group = api_get_group_entity(); + + if ($this->hasGroupSupport()) { + $name = implode( + '-', + [ + $course->getCode(), + $session ? $session->getName() : '', + $group ? $group->getName() : '', + ] + ); + + return $nameFilter($name); + } + + $name = implode( + '-', + [ + $course->getCode(), + $session ? $session->getName() : '', + ] + ); + + return $nameFilter($name); + } + + /** + * Returns a meeting "join" URL + * + * @param string The name of the meeting (usually the course code) + * + * @return mixed The URL to join the meeting, or false on error + * @todo implement moderator pass + * @assert ('') === false + * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false + */ + public function joinMeeting($meetingName) + { + if ($this->debug) { + error_log("joinMeeting: $meetingName"); + } + + if (empty($meetingName)) { + return false; + } + + $manager = $this->isConferenceManager(); + if ($manager) { + $pass = $this->getModMeetingPassword(); + } else { + $pass = $this->getUserMeetingPassword(); + } + + $meetingData = Database::select( + '*', + $this->table, + array( + 'where' => array( + 'meeting_name = ? AND status = 1' => array( + $meetingName, + ), + ), + ), + 'first' + ); + + if (empty($meetingData) || !is_array($meetingData)) { + if ($this->debug) { + error_log("meeting does not exist: $meetingName"); + } + + return false; + } + + $params = array( + 'meetingId' => $meetingData['remote_id'], + // -- REQUIRED - The unique id for the meeting + 'password' => $this->getModMeetingPassword() + // -- REQUIRED - The moderator password for the meeting + ); + + $meetingInfoExists = false; + $meetingIsRunningInfo = $this->getMeetingInfo($params); + if ($this->debug) { + error_log('Searching meeting with params:'); + error_log(print_r($params, 1)); + error_log('Result:'); + error_log(print_r($meetingIsRunningInfo, 1)); + } + + if ($meetingIsRunningInfo === false) { + // checking with the remote_id didn't work, so just in case and + // to provide backwards support, check with the id + $params = array( + 'meetingId' => $meetingData['id'], + // -- REQUIRED - The unique id for the meeting + 'password' => $this->getModMeetingPassword() + // -- REQUIRED - The moderator password for the meeting + ); + $meetingIsRunningInfo = $this->getMeetingInfo($params); + if ($this->debug) { + error_log('Searching meetingId with params:'); + error_log(print_r($params, 1)); + error_log('Result:'); + error_log(print_r($meetingIsRunningInfo, 1)); + } + } + + if (strval($meetingIsRunningInfo['returncode']) === 'SUCCESS' && + isset($meetingIsRunningInfo['meetingName']) && + !empty($meetingIsRunningInfo['meetingName']) + ) { + $meetingInfoExists = true; + } + + if ($this->debug) { + error_log( + "meeting is running: ".intval($meetingInfoExists) + ); + } + + $url = false; + if ($meetingInfoExists) { + $joinParams = [ + 'meetingId' => $meetingData['remote_id'], + // -- REQUIRED - A unique id for the meeting + 'username' => $this->userCompleteName, + //-- REQUIRED - The name that will display for the user in the meeting + 'password' => $pass, + //-- REQUIRED - The attendee or moderator password, depending on what's passed here + //'createTime' => api_get_utc_datetime(), //-- OPTIONAL - string. Leave blank ('') unless you set this correctly. + 'userID' => api_get_user_id(), + //-- OPTIONAL - string + 'webVoiceConf' => '', + ]; + $url = $this->api->getJoinMeetingURL($joinParams); + $url = $this->protocol.$url; + } + + if ($this->debug) { + error_log("return url :".$url); + } + + return $url; + } + + /** + * Checks whether a user is teacher in the current course + * @return bool True if the user can be considered a teacher in this course, false otherwise + */ + public function isConferenceManager() + { + if (api_is_coach() || api_is_platform_admin(false, true)) { + return true; + } + + if ($this->isGlobalConferencePerUserEnabled()) { + $currentUserId = api_get_user_id(); + if ($this->userId === $currentUserId) { + return true; + } else { + return false; + } + } + + $courseInfo = api_get_course_info(); + $groupId = api_get_group_id(); + if (!empty($groupId) && !empty($courseInfo)) { + $groupEnabled = api_get_course_plugin_setting('bbb', 'bbb_enable_conference_in_groups') === '1'; + if ($groupEnabled) { + $studentCanStartConference = api_get_course_plugin_setting( + 'bbb', + 'big_blue_button_students_start_conference_in_groups' + ) === '1'; + + if ($studentCanStartConference) { + $isSubscribed = GroupManager::is_user_in_group( + api_get_user_id(), + GroupManager::get_group_properties($groupId) + ); + if ($isSubscribed) { + return true; + } + } + } + } + + if (!empty($courseInfo)) { + return api_is_course_admin(); + } + + return false; + } + + /** + * Get information about the given meeting + * + * @param array ...? + * + * @return mixed Array of information on success, false on error + * @assert (array()) === false + */ + public function getMeetingInfo($params) + { + try { + $result = $this->api->getMeetingInfoWithXmlResponseArray($params); + if ($result == null) { + if ($this->debug) { + error_log("Failed to get any response. Maybe we can't contact the BBB server."); + } + } + + return $result; + } catch (Exception $e) { + if ($this->debug) { + error_log('Caught exception: ', $e->getMessage(), "\n"); + } + } + + return false; + } + + + /** + * @param int $meetingId + * @param int $userId + * + * @return array + */ + public function getMeetingParticipantInfo($meetingId, $userId) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_room', + array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])), + 'first' + ); + + if ($meetingData) { + return $meetingData; + } + + return []; + } + + /** + * Save a participant in a meeting room + * + * @param int $meetingId + * @param int $participantId + * + * @return false|int The last inserted ID. Otherwise return false + */ + public function saveParticipant($meetingId, $participantId) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_room', + [ + 'where' => [ + 'meeting_id = ? AND participant_id = ? AND close = ?' => [ + $meetingId, + $participantId, + BBBPlugin::ROOM_OPEN, + ], + ], + ] + ); + + foreach ($meetingData as $roomItem) { + $inAt = $roomItem['in_at']; + $outAt = $roomItem['out_at']; + $roomId = $roomItem['id']; + if (!empty($roomId)) { + if ($inAt != $outAt) { + Database::update( + 'plugin_bbb_room', + ['close' => BBBPlugin::ROOM_CLOSE], + ['id = ? ' => $roomId] + ); + } else { + Database::update( + 'plugin_bbb_room', + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE], + ['id = ? ' => $roomId] + ); + } + } + } + + $params = [ + 'meeting_id' => $meetingId, + 'participant_id' => $participantId, + 'in_at' => api_get_utc_datetime(), + 'out_at' => api_get_utc_datetime(), + 'close' => BBBPlugin::ROOM_OPEN, + ]; + + return Database::insert( + 'plugin_bbb_room', + $params + ); + } + + /** + * Tells whether the given meeting exists and is running + * (using course code as name) + * + * @param string $meetingName Meeting name (usually the course code) + * + * @return bool True if meeting exists, false otherwise + * @assert ('') === false + * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false + */ + public function meetingExists($meetingName) + { + $meetingData = $this->getMeetingByName($meetingName); + + return !empty($meetingData); + } + + /** + * @param string $meetingName + * + * @return array + */ + public function getMeetingByName($meetingName) + { + if (empty($meetingName)) { + return []; + } + + $courseId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1' => + array($courseId, $sessionId, $meetingName), + )); + + if ($this->hasGroupSupport()) { + $groupId = api_get_group_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 AND access_url = ?' => + array( + $courseId, + $sessionId, + $meetingName, + $groupId, + $this->accessUrl, + ), + ), + ); + } + + $meetingData = Database::select( + '*', + $this->table, + $conditions, + 'first' + ); + + if ($this->debug) { + error_log('meeting_exists '.print_r($meetingData, 1)); + } + + return $meetingData; + } + + /** + * Gets a list from the database of all meetings attached to a course with the given status + * @param int $courseId + * @param int $sessionId + * @param int $status 0 for closed meetings, 1 for open meetings + * + * @return array + */ + public function getAllMeetingsInCourse($courseId, $sessionId, $status) + { + $conditions = array( + 'where' => array( + 'status = ? AND c_id = ? AND session_id = ? ' => array( + $status, + $courseId, + $sessionId, + ), + ), + ); + + return Database::select( + '*', + $this->table, + $conditions + ); + } + + /** + * Gets all the course meetings saved in the plugin_bbb_meeting table and + * generate actionable links (join/close/delete/etc) + * + * @param int $courseId + * @param int $sessionId + * @param int $groupId + * @param bool $isAdminReport Optional. Set to true then the report is for admins + * @param array $dateRange Optional + * @param int $start Optional + * @param int $limit Optional + * + * @return array Array of current open meeting rooms + * @throws Exception + */ + public function getMeetings( + $courseId = 0, + $sessionId = 0, + $groupId = 0, + $isAdminReport = false, + $dateRange = [], + $start = 0, + $limit = 0, + $order = "ASC" + ) { + $em = Database::getManager(); + $manager = $this->isConferenceManager(); + + $conditions = []; + if ($courseId || $sessionId || $groupId) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? ' => array($courseId, $sessionId), + ), + ); + + if ($this->hasGroupSupport()) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND group_id = ? ' => array( + $courseId, + $sessionId, + $groupId, + ), + ), + ); + } + + if ($this->isGlobalConferencePerUserEnabled()) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND user_id = ?' => array( + $courseId, + $sessionId, + $this->userId, + ), + ), + ); + } + } + if ($this->isGlobalConference()) { + $conditions = array( + 'where' => array( + 'c_id = ? AND user_id = ?' => array( + 0, + $this->userId, + ), + ), + ); + } + + if (!empty($dateRange)) { + $dateStart = date_create($dateRange['search_meeting_start']); + $dateStart = date_format($dateStart, 'Y-m-d H:i:s'); + $dateEnd = date_create($dateRange['search_meeting_end']); + $dateEnd = $dateEnd->add(new DateInterval('P1D')); + $dateEnd = date_format($dateEnd, 'Y-m-d H:i:s'); + + $conditions = array( + 'where' => array( + 'created_at BETWEEN ? AND ? ' => array($dateStart, $dateEnd), + ), + ); + } + + $conditions['order'] = 'created_at ' . $order; + + if ($limit) { + $conditions['limit'] = "$start , $limit"; + } + + $meetingList = Database::select( + '*', + $this->table, + $conditions + ); + $isGlobal = $this->isGlobalConference(); + $newMeetingList = array(); + foreach ($meetingList as $meetingDB) { + $item = array(); + $item['metting_name'] = $meetingDB['meeting_name']; + $courseId = $meetingDB['c_id']; + $courseInfo = api_get_course_info_by_id($courseId); + $courseCode = ''; + if (!empty($courseInfo)) { + $courseCode = $courseInfo['code']; + } + + if ($manager) { + $pass = $meetingDB['moderator_pw']; + } else { + $pass = $meetingDB['attendee_pw']; + } + + $meetingBBB = $this->getMeetingInfo( + [ + 'meetingId' => $meetingDB['remote_id'], + 'password' => $pass, + ] + ); + + if ($meetingBBB === false) { + // Checking with the remote_id didn't work, so just in case and + // to provide backwards support, check with the id + $params = array( + 'meetingId' => $meetingDB['id'], + // -- REQUIRED - The unique id for the meeting + 'password' => $pass + // -- REQUIRED - The moderator password for the meeting + ); + $meetingBBB = $this->getMeetingInfo($params); + } + + if ($meetingDB['visibility'] == 0 && $this->isConferenceManager() === false) { + continue; + } + + $meetingBBB['end_url'] = $this->endUrl($meetingDB); + + if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') { + if ($meetingDB['status'] == 1 && $this->isConferenceManager()) { + $this->endMeeting($meetingDB['id'], $courseCode); + } + } else { + $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingDB); + } + + if ($meetingDB['record'] == 1) { + // backwards compatibility (when there was no remote ID) + $mId = $meetingDB['remote_id']; + if (empty($mId)) { + $mId = $meetingDB['id']; + } + if (empty($mId)) { + // if the id is still empty (should *never* occur as 'id' is + // the table's primary key), skip this conference + continue; + } + + $record = []; + $recordingParams = ['meetingId' => $mId]; + $records = $this->api->getRecordingsWithXmlResponseArray($recordingParams); + + if (!empty($records)) { + if (!isset($records['messageKey']) || $records['messageKey'] !== 'noRecordings') { + $record = end($records); + if (!is_array($record) || !isset($record['recordId'])) { + continue; + } + + if (!empty($record['playbackFormat'])) { + $this->updateMeetingVideoUrl($meetingDB['id'], $record['playbackFormatUrl']); + } + } + } + + if (isset($record['playbackFormat']) && !empty($record['playbackFormat'])) { + $recordLink = []; + foreach ($record['playbackFormat'] as $format) { + $this->insertMeetingFormat(intval($meetingDB['id']), $format->type->__toString(), $format->url->__toString()); + $recordLink['record'][] = 1; + $recordLink[] = Display::url( + $this->plugin->get_lang($format->type->__toString()), + $format->url->__toString(), + ['target' => '_blank', 'class' => 'btn btn-default'] + ); + } + } else { + $recordLink = $this->plugin->get_lang('NoRecording'); + } + + if ($isAdminReport) { + $this->forceCIdReq( + $courseInfo['code'], + $meetingDB['session_id'], + $meetingDB['group_id'] + ); + } + + $actionLinks = $this->getActionLinks( + $meetingDB, + $record, + $isGlobal, + $isAdminReport + ); + $item['show_links'] = $recordLink; + $item['record'] = true; + } else { + $actionLinks = $this->getActionLinks( + $meetingDB, + [], + $isGlobal, + $isAdminReport + ); + + $item['show_links'] = $this->plugin->get_lang('NoRecording'); + $item['record'] = false; + } + + $item['action_links'] = implode(PHP_EOL, $actionLinks); + $item['created_at'] = api_convert_and_format_date($meetingDB['created_at']); + // created_at + $meetingDB['created_at'] = $item['created_at']; //avoid overwrite in array_merge() below + + $item['closed_at'] = ''; + if (!empty($meetingDB['closed_at'])) { + $item['closed_at'] = api_convert_and_format_date($meetingDB['closed_at']); + $meetingDB['closed_at'] = $item['closed_at']; + } + + $item['publish_url'] = $this->publishUrl($meetingDB); + $item['unpublish_url'] = $this->unPublishUrl($meetingBBB); + + if ($meetingDB['status'] == 1) { + $joinParams = [ + 'meetingId' => $meetingDB['remote_id'], + //-- REQUIRED - A unique id for the meeting + 'username' => $this->userCompleteName, + //-- REQUIRED - The name that will display for the user in the meeting + 'password' => $pass, + //-- REQUIRED - The attendee or moderator password, depending on what's passed here + 'createTime' => '', + //-- OPTIONAL - string. Leave blank ('') unless you set this correctly. + 'userID' => '', + // -- OPTIONAL - string + 'webVoiceConf' => '', + ]; + $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams); + } + $item = array_merge($item, $meetingDB, $meetingBBB); + + $item['course'] = $em->find('ChamiloCoreBundle:Course', $item['c_id']); + $item['session'] = $em->find('ChamiloCoreBundle:Session', $item['session_id']); + $newMeetingList[] = $item; + } + + return $newMeetingList; + } + + /** + * Counts all the course meetings saved in the plugin_bbb_meeting table. + * + * @param int $courseId + * @param int $sessionId + * @param int $groupId + * @param array $dateRange + * + * @return int Count of meetings + * @throws Exception + */ + public function getCountMeetings( + $courseId = 0, + $sessionId = 0, + $groupId = 0, + $dateRange = [] + ) { + $conditions = []; + if ($courseId || $sessionId || $groupId) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? ' => array($courseId, $sessionId), + ), + ); + + if ($this->hasGroupSupport()) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND group_id = ? ' => array( + $courseId, + $sessionId, + $groupId, + ), + ), + ); + } + + if ($this->isGlobalConferencePerUserEnabled()) { + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND user_id = ?' => array( + $courseId, + $sessionId, + $this->userId, + ), + ), + ); + } + } + + if (!empty($dateRange)) { + $dateStart = date_create($dateRange['search_meeting_start']); + $dateStart = date_format($dateStart, 'Y-m-d H:i:s'); + $dateEnd = date_create($dateRange['search_meeting_end']); + $dateEnd = $dateEnd->add(new DateInterval('P1D')); + $dateEnd = date_format($dateEnd, 'Y-m-d H:i:s'); + + $conditions = array( + 'where' => array( + 'created_at BETWEEN ? AND ? ' => array($dateStart, $dateEnd), + ), + ); + } + + $row = Database::select( + 'count(*) as count', + $this->table, + $conditions, + 'first' + ); + + return $row['count']; + } + + /** + * @param array $meeting + * + * @return string + */ + public function endUrl($meeting) + { + if (!isset($meeting['id'])) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id']; + } + + /** + * Closes a meeting (usually when the user click on the close button from + * the conferences listing. + * + * @param string The internal ID of the meeting (id field for this meeting) + * @param string $courseCode + * + * @return void + * @assert (0) === false + */ + public function endMeeting($id, $courseCode = null) + { + if (empty($id)) { + return false; + } + + $meetingData = Database::select( + '*', + $this->table, + array('where' => array('id = ?' => array($id))), + 'first' + ); + $manager = $this->isConferenceManager(); + if ($manager) { + $pass = $meetingData['moderator_pw']; + } else { + $pass = $meetingData['attendee_pw']; + } + + Event::addEvent( + 'bbb_end_meeting', + 'meeting_id', + (int) $id, + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + + $endParams = array( + 'meetingId' => $meetingData['remote_id'], // REQUIRED - We have to know which meeting to end. + 'password' => $pass, // REQUIRED - Must match moderator pass for meeting. + ); + $this->api->endMeetingWithXmlResponseArray($endParams); + Database::update( + $this->table, + array('status' => 0, 'closed_at' => api_get_utc_datetime()), + array('id = ? ' => $id) + ); + + // Update users with in_at y ou_at field equal + $roomTable = Database::get_main_table('plugin_bbb_room'); + $conditions['where'] = ['meeting_id=? AND in_at=out_at AND close=?' => [$id, BBBPlugin::ROOM_OPEN]]; + $roomList = Database::select( + '*', + $roomTable, + $conditions + ); + + foreach ($roomList as $roomDB) { + $roomId = $roomDB['id']; + if (!empty($roomId)) { + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE], + ['id = ? ' => $roomId] + ); + } + } + + // Close all meeting rooms with meeting ID + Database::update( + $roomTable, + ['close' => BBBPlugin::ROOM_CLOSE], + ['meeting_id = ? ' => $id] + ); + } + + /** + * @param array $meeting + * @param array $record + * + * @return string + */ + public function addToCalendarUrl($meeting, $record = []) + { + $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : ''; + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams( + ).'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url; + } + + /** + * @param int $meetingId + * @param string $videoUrl + * + * @return bool|int + */ + public function updateMeetingVideoUrl($meetingId, $videoUrl) + { + return Database::update( + 'plugin_bbb_meeting', + ['video_url' => $videoUrl], + ['id = ?' => intval($meetingId)] + ); + } + + /** + * @param int $meetingId + * @param string $formatType + * @param string $resourceUrl + * + * @return bool|int + */ + public function insertMeetingFormat(int $meetingId, string $formatType, string $resourceUrl) + { + $em = Database::getManager(); + $sm = $em->getConnection()->getSchemaManager(); + if ($sm->tablesExist('plugin_bbb_meeting_format')) { + return Database::insert( + 'plugin_bbb_meeting_format', + [ + 'format_type' => $formatType, + 'resource_url' => $resourceUrl, + 'meeting_id' => $meetingId + ] + ); + } + + } + + /** + * Force the course, session and/or group IDs + * + * @param string $courseCode + * @param int $sessionId + * @param int $groupId + */ + public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0) + { + $this->courseCode = $courseCode; + $this->sessionId = (int) $sessionId; + $this->groupId = (int) $groupId; + } + + /** + * @param array $meetingInfo + * @param array $recordInfo + * @param bool $isGlobal + * @param bool $isAdminReport + * + * @return array + */ + private function getActionLinks( + $meetingInfo, + $recordInfo, + $isGlobal = false, + $isAdminReport = false + ) { + $isVisible = $meetingInfo['visibility'] != 0; + $linkVisibility = $isVisible + ? Display::url( + Display::return_icon('visible.png', get_lang('MakeInvisible')), + $this->unPublishUrl($meetingInfo) + ) + : Display::url( + Display::return_icon('invisible.png', get_lang('MakeVisible')), + $this->publishUrl($meetingInfo) + ); + + $links = []; + if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) { + if (!empty($recordInfo)) { + $links[] = Display::url( + Display::return_icon('reload.png', get_lang('RegenerateRecord')), + $this->regenerateRecordUrl($meetingInfo, $recordInfo) + ); + } else { + $links[] = Display::url( + Display::return_icon('reload.png', get_lang('RegenerateRecord')), + $this->regenerateRecordUrlFromMeeting($meetingInfo) + ); + } + } + + if (empty($recordInfo)) { + if (!$isAdminReport) { + if ($meetingInfo['status'] == 0) { + $links[] = Display::url( + Display::return_icon('delete.png', get_lang('Delete')), + $this->deleteRecordUrl($meetingInfo) + ); + $links[] = $linkVisibility; + } + + return $links; + } else { + $links[] = Display::url( + Display::return_icon('course_home.png', get_lang('GoToCourse')), + $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id']) + ); + + return $links; + } + } + + if (!$isGlobal) { + $links[] = Display::url( + Display::return_icon('link.gif', get_lang('UrlMeetingToShare')), + $this->copyToRecordToLinkTool($meetingInfo) + ); + $links[] = Display::url( + Display::return_icon('agenda.png', get_lang('AddToCalendar')), + $this->addToCalendarUrl($meetingInfo, $recordInfo) + ); + } + + $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false; + + if ($hide == false) { + if ($meetingInfo['has_video_m4v']) { + foreach ($recordInfo['playbackFormat'] as $format) { + if ($format->type->__toString() == 'video') { + $recordingUrl = $format->url->__toString(); + } + } + $links[] = Display::url( + Display::return_icon('save.png', get_lang('DownloadFile')), + $recordingUrl.'video-0.m4v', + ['target' => '_blank'] + ); + } else { + $links[] = Display::url( + Display::return_icon('save.png', get_lang('DownloadFile')), + '#', + [ + 'id' => "btn-check-meeting-video-{$meetingInfo['id']}", + 'class' => 'check-meeting-video', + 'data-id' => $meetingInfo['id'], + ] + ); + } + } + + + if (!$isAdminReport) { + $links[] = Display::url( + Display::return_icon('delete.png', get_lang('Delete')), + $this->deleteRecordUrl($meetingInfo) + ); + $links[] = $linkVisibility; + } else { + $links[] = Display::url( + Display::return_icon('course_home.png', get_lang('GoToCourse')), + $this->getListingUrl($meetingInfo['c_id'], $meetingInfo['session_id'], $meetingInfo['group_id']) + ); + } + + + return $links; + } + + /** + * @param array $meeting + * + * @return string + */ + public function unPublishUrl($meeting) + { + if (!isset($meeting['id'])) { + return null; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams( + ).'&action=unpublish&id='.$meeting['id']; + } + + /** + * @param array $meeting + * + * @return string + */ + public function publishUrl($meeting) + { + if (!isset($meeting['id'])) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams( + ).'&action=publish&id='.$meeting['id']; + } + + /** + * @param array $meeting + * @param array $recordInfo + * + * @return string + */ + public function regenerateRecordUrl($meeting, $recordInfo) + { + if ($this->plugin->get('allow_regenerate_recording') !== 'true') { + return ''; + } + + if (!isset($meeting['id'])) { + return ''; + } + + if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(). + '&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId']; + } + + /** + * @param array $meeting + * + * @return string + */ + public function regenerateRecordUrlFromMeeting($meeting) + { + if ($this->plugin->get('allow_regenerate_recording') !== 'true') { + return ''; + } + + if (!isset($meeting['id'])) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams(). + '&action=regenerate_record&id='.$meeting['id']; + } + + /** + * @param array $meeting + * + * @return string + */ + public function deleteRecordUrl($meeting) + { + if (!isset($meeting['id'])) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams( + ).'&action=delete_record&id='.$meeting['id']; + } + + /** + * @param array $meeting + * + * @return string + */ + public function copyToRecordToLinkTool($meeting) + { + if (!isset($meeting['id'])) { + return ''; + } + + return api_get_path(WEB_PLUGIN_PATH). + 'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id']; + } + + /** + * Function disabled + */ + public function publishMeeting($id) + { + //return BigBlueButtonBN::setPublishRecordings($id, 'true', $this->url, $this->salt); + if (empty($id)) { + return false; + } + $id = intval($id); + Database::update($this->table, array('visibility' => 1), array('id = ? ' => $id)); + + return true; + } + + /** + * Function disabled + */ + public function unpublishMeeting($id) + { + //return BigBlueButtonBN::setPublishRecordings($id, 'false', $this->url, $this->salt); + if (empty($id)) { + return false; + } + $id = intval($id); + Database::update($this->table, array('visibility' => 0), array('id = ?' => $id)); + + return true; + } + + /** + * Get users online in the current course room. + * + * @return int The number of users currently connected to the videoconference + * @assert () > -1 + */ + public function getUsersOnlineInCurrentRoom() + { + $courseId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND status = 1 AND access_url = ?' => array( + $courseId, + $sessionId, + $this->accessUrl, + ), + ), + ); + + if ($this->hasGroupSupport()) { + $groupId = api_get_group_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND group_id = ? AND status = 1 AND access_url = ?' => array( + $courseId, + $sessionId, + $groupId, + $this->accessUrl, + ), + ), + ); + } + + if ($this->isGlobalConferencePerUserEnabled()) { + $conditions = array( + 'where' => array( + 'user_id = ? AND status = 1 AND access_url = ?' => array( + $this->userId, + $this->accessUrl, + ), + ), + ); + } + + $meetingData = Database::select( + '*', + $this->table, + $conditions, + 'first' + ); + + if (empty($meetingData)) { + return 0; + } + $pass = $meetingData['moderator_pw']; + $info = $this->getMeetingInfo(array('meetingId' => $meetingData['remote_id'], 'password' => $pass)); + if ($info === false) { + //checking with the remote_id didn't work, so just in case and + // to provide backwards support, check with the id + $params = array( + 'meetingId' => $meetingData['id'], + // -- REQUIRED - The unique id for the meeting + 'password' => $pass + // -- REQUIRED - The moderator password for the meeting + ); + $info = $this->getMeetingInfo($params); + } + + if (!empty($info) && isset($info['participantCount'])) { + return $info['participantCount']; + } + + return 0; + } + + /** + * @param int $id + * @param string $recordId + * + * @return bool + */ + public function regenerateRecording($id, $recordId = '') + { + if ($this->plugin->get('allow_regenerate_recording') !== 'true') { + return false; + } + + if (empty($id)) { + return false; + } + + $meetingData = Database::select( + '*', + $this->table, + array('where' => array('id = ?' => array($id))), + 'first' + ); + + Event::addEvent( + 'bbb_regenerate_record', + 'record_id', + (int) $recordId, + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + + // Check if there are recordings for this meeting + $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]); + if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') { + // Regenerate the meeting id + if (!empty($meetingData['internal_meeting_id'])) { + return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]); + } + + /*$pass = $this->getModMeetingPassword(); + $info = $this->getMeetingInfo(['meetingId' => $meetingData['remote_id'], 'password' => $pass]); + if (!empty($info) && isset($info['internalMeetingID'])) { + return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]); + }*/ + + return false; + } else { + if (!empty($recordings['records'])) { + $recordExists = false; + foreach ($recordings['records'] as $record) { + if ($recordId == $record['recordId']) { + $recordExists = true; + break; + } + } + + if ($recordExists) { + return $this->api->generateRecording(['recordId' => $recordId]); + } + } + } + + return false; + } + + /** + * Deletes a recording of a meeting + * + * @param int $id ID of the recording + * + * @return bool + * + * @assert () === false + * @todo Also delete links and agenda items created from this recording + */ + public function deleteRecording($id) + { + if (empty($id)) { + return false; + } + + $meetingData = Database::select( + '*', + $this->table, + array('where' => array('id = ?' => array($id))), + 'first' + ); + + Event::addEvent( + 'bbb_delete_record', + 'meeting_id', + $id, + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + + $delete = false; + $recordings = []; + // Check if there are recordings for this meeting + if (!empty($meetingData['remote_id'])) { + Event::addEvent( + 'bbb_delete_record', + 'remote_id', + $meetingData['remote_id'], + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]); + } + if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] == 'noRecordings') { + $delete = true; + } else { + if (!empty($recordings['records'])) { + $recordsToDelete = []; + foreach ($recordings['records'] as $record) { + $recordsToDelete[] = $record['recordId']; + } + $delete = true; + if (!empty($recordsToDelete)) { + $recordingParams = ['recordId' => implode(',', $recordsToDelete)]; + Event::addEvent( + 'bbb_delete_record', + 'record_id_list', + implode(',', $recordsToDelete), + null, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams); + if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') { + $delete = true; + } + } + } + } + + if ($delete) { + Database::delete( + 'plugin_bbb_room', + array('meeting_id = ?' => array($id)) + ); + + Database::delete( + $this->table, + array('id = ?' => array($id)) + ); + } + + return $delete; + } + + /** + * Creates a link in the links tool from the given videoconference recording + * + * @param int $id ID of the item in the plugin_bbb_meeting table + * @param string Hash identifying the recording, as provided by the API + * + * @return mixed ID of the newly created link, or false on error + * @assert (null, null) === false + * @assert (1, null) === false + * @assert (null, 'abcdefabcdefabcdefabcdef') === false + */ + public function copyRecordingToLinkTool($id) + { + if (empty($id)) { + return false; + } + //$records = BigBlueButtonBN::getRecordingsUrl($id); + $meetingData = Database::select( + '*', + $this->table, + array('where' => array('id = ?' => array($id))), + 'first' + ); + + $records = $this->api->getRecordingsWithXmlResponseArray( + array('meetingId' => $meetingData['remote_id']) + ); + + if (!empty($records)) { + if (isset($records['message']) && !empty($records['message'])) { + if ($records['messageKey'] == 'noRecordings') { + $recordArray[] = $this->plugin->get_lang('NoRecording'); + } else { + //$recordArray[] = $records['message']; + } + + return false; + } else { + $record = $records[0]; + if (is_array($record) && isset($record['recordId'])) { + $url = $record['playbackFormatUrl']; + $link = new Link(); + $params['url'] = $url; + $params['title'] = $meetingData['meeting_name']; + $id = $link->save($params); + + return $id; + } + } + } + + return false; + } + + /** + * Checks if the video conference server is running. + * Function currently disabled (always returns 1) + * @return bool True if server is running, false otherwise + * @assert () === false + */ + public function isServerRunning() + { + return true; + //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url); + } + + /** + * Checks if the video conference plugin is properly configured + * @return bool True if plugin has a host and a salt, false otherwise + * @assert () === false + */ + public function isServerConfigured() + { + $host = $this->plugin->get('host'); + + if (empty($host)) { + return false; + } + + $salt = $this->plugin->get('salt'); + + if (empty($salt)) { + return false; + } + + return true; + //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url); + } + + /** + * Get active session in the all platform + */ + public function getActiveSessionsCount() + { + $meetingList = Database::select( + 'count(id) as count', + $this->table, + array('where' => array('status = ? AND access_url = ?' => array(1, $this->accessUrl))), + 'first' + ); + + return $meetingList['count']; + } + + /** + * Get active session in the all platform + * + * @param boolean $allSites Parameter to indicate whether to get the result from all sites + * + * @return array + */ + public function getActiveSessions(bool $allSites = false): array + { + $where = ['where' => ['status = ?' => 1]]; + + if (!$allSites) { + $where['where'][' AND access_url = ?'] = $this->accessUrl; + } + + return Database::select( + '*', + $this->table, + $where + ); + } + + /** + * @param string $url + */ + public function redirectToBBB($url) + { + if (file_exists(__DIR__.'/../config.vm.php')) { + // Using VM + echo Display::url($this->plugin->get_lang('ClickToContinue'), $url); + exit; + } else { + // Classic + header("Location: $url"); + exit; + } + } + + /** + * @return string + */ + public function getConferenceUrl() + { + return api_get_path(WEB_PLUGIN_PATH).'bbb/start.php?launch=1&'.$this->getUrlParams(); + } + + /** + * Get the meeting info from DB by its name + * + * @param string $name + * + * @return array + */ + public function findMeetingByName($name) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_meeting', + array('where' => array('meeting_name = ? AND status = 1 ' => $name)), + 'first' + ); + + return $meetingData; + } + + /** + * Get the meeting info from DB by its name + * + * @param int $id + * + * @return array + */ + public function getMeeting($id) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_meeting', + array('where' => array('id = ?' => $id)), + 'first' + ); + + return $meetingData; + } + + /** + * Get the meeting info. + * + * @param int $id + * + * @return array + */ + public function getMeetingByRemoteId($id) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_meeting', + array('where' => array('remote_id = ?' => $id)), + 'first' + ); + + return $meetingData; + } + + /** + * @param int $meetingId + * + * @return array + */ + public function findConnectedMeetingParticipants($meetingId) + { + $meetingData = Database::select( + '*', + 'plugin_bbb_room', + array('where' => array('meeting_id = ? AND in_at IS NOT NULL' => $meetingId)) + ); + $participantIds = []; + $return = []; + + foreach ($meetingData as $participantInfo) { + if (in_array($participantInfo['participant_id'], $participantIds)) { + continue; + } + + $participantIds[] = $participantInfo['participant_id']; + + $return[] = [ + 'id' => $participantInfo['id'], + 'meeting_id' => $participantInfo['meeting_id'], + 'participant' => api_get_user_entity($participantInfo['participant_id']), + 'in_at' => $participantInfo['in_at'], + 'out_at' => $participantInfo['out_at'], + ]; + } + + return $return; + } + + /** + * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated + * + * @param int $meetingId + * + * @return bool + */ + public function checkDirectMeetingVideoUrl($meetingId) + { + $meetingInfo = Database::select( + '*', + 'plugin_bbb_meeting', + [ + 'where' => ['id = ?' => intval($meetingId)], + ], + 'first' + ); + + if (!isset($meetingInfo['video_url'])) { + return false; + } + + $hasCapture = SocialManager::verifyUrl($meetingInfo['video_url'].'/capture.m4v'); + + if ($hasCapture) { + return Database::update( + 'plugin_bbb_meeting', + ['has_video_m4v' => true], + ['id = ?' => intval($meetingId)] + ); + } + + return $hasCapture; + } +} diff --git a/plugin/bbb/lib/bbb_api.php b/plugin/bbb/lib/bbb_api.php new file mode 100644 index 000000000..5a4ec9239 --- /dev/null +++ b/plugin/bbb/lib/bbb_api.php @@ -0,0 +1,697 @@ +_securitySalt = CONFIG_SECURITY_SALT; + $this->_bbbServerBaseUrl = CONFIG_SERVER_BASE_URL; + $this->_bbbServerProtocol = CONFIG_SERVER_PROTOCOL; + } + + private function _processXmlResponse($url) + { + /* + A private utility method used by other public methods to process XML responses. + */ + if (extension_loaded('curl')) { + $ch = curl_init() or die ( curl_error($ch) ); + $timeout = 10; + curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt( $ch, CURLOPT_URL, $this->_bbbServerProtocol.$url ); + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); + curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout); + // Following redirect required to use Scalelite, BBB's Load Balancer + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true); + $data = curl_exec( $ch ); + curl_close( $ch ); + + if ($data) { + return (new SimpleXMLElement($data)); + } else { + return false; + } + } + return (simplexml_load_file($url)); + } + + private function _requiredParam($param) { + /* Process required params and throw errors if we don't get values */ + if ((isset($param)) && ($param != '')) { + return $param; + } + elseif (!isset($param)) { + throw new Exception('Missing parameter.'); + } + else { + throw new Exception(''.$param.' is required.'); + } + } + + private function _optionalParam($param) { + /* Pass most optional params through as set value, or set to '' */ + /* Don't know if we'll use this one, but let's build it in case. */ + if ((isset($param)) && ($param != '')) { + return $param; + } + else { + $param = ''; + return $param; + } + } + + /* __________________ BBB ADMINISTRATION METHODS _________________ */ + /* The methods in the following section support the following categories of the BBB API: + -- create + -- join + -- end + */ + + public function getCreateMeetingUrl($creationParams) { + /* + USAGE: + (see $creationParams array in createMeetingArray method.) + */ + $this->_meetingId = $this->_requiredParam($creationParams['meetingId']); + $this->_meetingName = $this->_requiredParam($creationParams['meetingName']); + // Set up the basic creation URL: + $creationUrl = $this->_bbbServerBaseUrl."api/create?"; + // Add params: + $params = + 'name='.urlencode($this->_meetingName). + '&meetingID='.urlencode($this->_meetingId). + '&attendeePW='.urlencode($creationParams['attendeePw']). + '&moderatorPW='.urlencode($creationParams['moderatorPw']). + '&dialNumber='.urlencode($creationParams['dialNumber']). + '&voiceBridge='.urlencode($creationParams['voiceBridge']). + '&webVoice='.urlencode($creationParams['webVoice']). + '&logoutURL='.urlencode($creationParams['logoutUrl']). + '&maxParticipants='.urlencode($creationParams['maxParticipants']). + '&record='.urlencode($creationParams['record']). + '&duration='.urlencode($creationParams['duration']). + '&meta_OriginURL'.urlencode($creationParams['meta_OriginURL']) + ; + + //'&meta_category='.urlencode($creationParams['meta_category']); + $welcomeMessage = $creationParams['welcomeMsg']; + if (trim($welcomeMessage)) { + $params .= '&welcome='.urlencode($welcomeMessage); + } + + // Return the complete URL: + return ($creationUrl.$params.'&checksum='.sha1("create".$params.$this->_securitySalt)); + } + + public function createMeetingWithXmlResponseArray($creationParams) + { + /* + USAGE: + $creationParams = array( + 'name' => 'Meeting Name', -- A name for the meeting (or username) + 'meetingId' => '1234', -- A unique id for the meeting + 'attendeePw' => 'ap', -- Set to 'ap' and use 'ap' to join = no user pass required. + 'moderatorPw' => 'mp', -- Set to 'mp' and use 'mp' to join = no user pass required. + 'welcomeMsg' => '', -- ''= use default. Change to customize. + 'dialNumber' => '', -- The main number to call into. Optional. + 'voiceBridge' => '', -- 5 digits PIN to join voice. Required. + 'webVoice' => '', -- Alphanumeric to join voice. Optional. + 'logoutUrl' => '', -- Default in bigbluebutton.properties. Optional. + 'maxParticipants' => '-1', -- Optional. -1 = unlimitted. Not supported in BBB. [number] + 'record' => 'false', -- New. 'true' will tell BBB to record the meeting. + 'duration' => '0', -- Default = 0 which means no set duration in minutes. [number] + 'meta_category' => '', -- Use to pass additional info to BBB server. See API docs to enable. + ); + */ + $xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams)); + + if ($xml) { + if ($xml->meetingID) { + return [ + 'returncode' => $xml->returncode->__toString(), + 'message' => $xml->message->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'meetingId' => $xml->meetingID->__toString(), + 'attendeePw' => $xml->attendeePW->__toString(), + 'moderatorPw' => $xml->moderatorPW->__toString(), + 'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(), + 'createTime' => $xml->createTime->__toString(), + 'internalMeetingID' => $xml->internalMeetingID->__toString(), + ]; + } else { + return [ + 'returncode' => $xml->returncode->__toString(), + 'message' => $xml->message->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + ]; + } + } else { + return null; + } + } + + public function getJoinMeetingURL($joinParams) + { + /* + NOTE: At this point, we don't use a corresponding joinMeetingWithXmlResponse here because the API + doesn't respond on success, but you can still code that method if you need it. Or, you can take the URL + that's returned from this method and simply send your users off to that URL in your code. + USAGE: + $joinParams = array( + 'meetingId' => '1234', -- REQUIRED - A unique id for the meeting + 'username' => 'Jane Doe', -- REQUIRED - The name that will display for the user in the meeting + 'password' => 'ap', -- REQUIRED - The attendee or moderator password, depending on what's passed here + 'createTime' => '', -- OPTIONAL - string. Leave blank ('') unless you set this correctly. + 'userID' => '', -- OPTIONAL - string + 'webVoiceConf' => '' -- OPTIONAL - string + ); + */ + $this->_meetingId = $this->_requiredParam($joinParams['meetingId']); + $this->_username = $this->_requiredParam($joinParams['username']); + $this->_password = $this->_requiredParam($joinParams['password']); + + // Establish the basic join URL: + $joinUrl = $this->_bbbServerBaseUrl."api/join?"; + + // Add parameters to the URL: + $params = + 'meetingID='.urlencode($this->_meetingId). + '&fullName='.urlencode($this->_username). + '&password='.urlencode($this->_password). + '&userID='.urlencode($joinParams['userID']). + '&webVoiceConf='.urlencode($joinParams['webVoiceConf']) + ; + + // Only use createTime if we really want to use it. If it's '', then don't pass it: + if ((isset($joinParams['createTime']) && $joinParams['createTime'] != '')) { + $params .= '&createTime='.urlencode($joinParams['createTime']); + } + + /*if (isset($joinParams['interface']) && (int) $joinParams['interface'] === BBBPlugin::INTERFACE_HTML5) { + $bbbHost = api_remove_trailing_slash(CONFIG_SERVER_URL_WITH_PROTOCOL); + if (preg_match('#/bigbluebutton$#', $bbbHost)) { + $bbbHost = preg_replace('#/bigbluebutton$#', '', $bbbHost); + } + $params .= '&redirectClient=true&clientURL='.$bbbHost.'/html5client/join'; + }*/ + + // Return the URL: + return $joinUrl.$params.'&checksum='.sha1('join'.$params.$this->_securitySalt); + } + + public function getEndMeetingURL($endParams) + { + /* USAGE: + $endParams = array ( + 'meetingId' => '1234', -- REQUIRED - The unique id for the meeting + 'password' => 'mp' -- REQUIRED - The moderator password for the meeting + ); + */ + $this->_meetingId = $this->_requiredParam($endParams['meetingId']); + $this->_password = $this->_requiredParam($endParams['password']); + $endUrl = $this->_bbbServerBaseUrl."api/end?"; + $params = + 'meetingID='.urlencode($this->_meetingId). + '&password='.urlencode($this->_password) + ; + + return ($endUrl.$params.'&checksum='.sha1("end".$params.$this->_securitySalt)); + } + + public function endMeetingWithXmlResponseArray($endParams) { + /* USAGE: + $endParams = array ( + 'meetingId' => '1234', -- REQUIRED - The unique id for the meeting + 'password' => 'mp' -- REQUIRED - The moderator password for the meeting + ); + */ + $xml = $this->_processXmlResponse($this->getEndMeetingURL($endParams)); + if ($xml) { + return array( + 'returncode' => $xml->returncode->__toString(), + 'message' => $xml->message->__toString(), + 'messageKey' => $xml->messageKey->__toString() + ); + } + else { + return null; + } + + } + + /* __________________ BBB MONITORING METHODS _________________ */ + /* The methods in the following section support the following categories of the BBB API: + -- isMeetingRunning + -- getMeetings + -- getMeetingInfo + */ + public function getIsMeetingRunningUrl($meetingId) { + /* USAGE: + $meetingId = '1234' -- REQUIRED - The unique id for the meeting + */ + $this->_meetingId = $this->_requiredParam($meetingId); + $runningUrl = $this->_bbbServerBaseUrl."api/isMeetingRunning?"; + $params = + 'meetingID='.urlencode($this->_meetingId); + return ($runningUrl.$params.'&checksum='.sha1("isMeetingRunning".$params.$this->_securitySalt)); + } + + public function isMeetingRunningWithXmlResponseArray($meetingId) { + /* USAGE: + $meetingId = '1234' -- REQUIRED - The unique id for the meeting + */ + $xml = $this->_processXmlResponse($this->getIsMeetingRunningUrl($meetingId)); + if($xml) { + return array( + 'returncode' => $xml->returncode->__toString(), + 'running' => $xml->running->__toString() // -- Returns true/false. + ); + } + else { + return null; + } + } + + public function getGetMeetingsUrl() { + /* Simply formulate the getMeetings URL + We do this in a separate function so we have the option to just get this + URL and print it if we want for some reason. + */ + $getMeetingsUrl = $this->_bbbServerBaseUrl."api/getMeetings?checksum=".sha1("getMeetings".$this->_securitySalt); + return $getMeetingsUrl; + } + + public function getMeetingsWithXmlResponseArray() + { + /* USAGE: + We don't need to pass any parameters with this one, so we just send the query URL off to BBB + and then handle the results that we get in the XML response. + */ + $xml = $this->_processXmlResponse($this->getGetMeetingsUrl()); + if($xml) { + // If we don't get a success code, stop processing and return just the returncode: + if ($xml->returncode != 'SUCCESS') { + $result = array( + 'returncode' => $xml->returncode->__toString() + ); + return $result; + } + elseif ($xml->messageKey == 'noMeetings') { + /* No meetings on server, so return just this info: */ + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + return $result; + } + else { + // In this case, we have success and meetings. First return general response: + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + // Then interate through meeting results and return them as part of the array: + foreach ($xml->meetings->meeting as $m) { + $result[] = array( + 'meetingId' => $m->meetingID->__toString(), + 'meetingName' => $m->meetingName->__toString(), + 'createTime' => $m->createTime->__toString(), + 'attendeePw' => $m->attendeePW->__toString(), + 'moderatorPw' => $m->moderatorPW->__toString(), + 'hasBeenForciblyEnded' => $m->hasBeenForciblyEnded->__toString(), + 'running' => $m->running->__toString() + ); + } + return $result; + } + } + else { + return null; + } + + } + + public function getMeetingInfoUrl($infoParams) { + /* USAGE: + $infoParams = array( + 'meetingId' => '1234', -- REQUIRED - The unique id for the meeting + 'password' => 'mp' -- REQUIRED - The moderator password for the meeting + ); + */ + $this->_meetingId = $this->_requiredParam($infoParams['meetingId']); + $this->_password = $this->_requiredParam($infoParams['password']); + $infoUrl = $this->_bbbServerBaseUrl."api/getMeetingInfo?"; + $params = + 'meetingID='.urlencode($this->_meetingId). + '&password='.urlencode($this->_password); + return ($infoUrl.$params.'&checksum='.sha1("getMeetingInfo".$params.$this->_securitySalt)); + } + + public function getMeetingInfoWithXmlResponseArray($infoParams) { + /* USAGE: + $infoParams = array( + 'meetingId' => '1234', -- REQUIRED - The unique id for the meeting + 'password' => 'mp' -- REQUIRED - The moderator password for the meeting + ); + */ + $xml = $this->_processXmlResponse($this->getMeetingInfoUrl($infoParams)); + if($xml) { + // If we don't get a success code or messageKey, find out why: + if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) { + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + return $result; + } else { + // In this case, we have success and meeting info: + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'meetingName' => $xml->meetingName->__toString(), + 'meetingId' => $xml->meetingID->__toString(), + 'createTime' => $xml->createTime->__toString(), + 'voiceBridge' => $xml->voiceBridge->__toString(), + 'attendeePw' => $xml->attendeePW->__toString(), + 'moderatorPw' => $xml->moderatorPW->__toString(), + 'running' => $xml->running->__toString(), + 'recording' => $xml->recording->__toString(), + 'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(), + 'startTime' => $xml->startTime->__toString(), + 'endTime' => $xml->endTime->__toString(), + 'participantCount' => $xml->participantCount->__toString(), + 'maxUsers' => $xml->maxUsers->__toString(), + 'moderatorCount' => $xml->moderatorCount->__toString(), + 'internalMeetingID' => $xml->internalMeetingID->__toString() + ); + + // Then interate through attendee results and return them as part of the array: + foreach ($xml->attendees->attendee as $a) { + $result[] = array( + 'userId' => $a->userID->__toString(), + 'fullName' => $a->fullName->__toString(), + 'role' => $a->role->__toString() + ); + } + return $result; + } + } + else { + return null; + } + + } + + /* __________________ BBB RECORDING METHODS _________________ */ + /* The methods in the following section support the following categories of the BBB API: + -- getRecordings + -- publishRecordings + -- deleteRecordings + */ + + public function getRecordingsUrl($recordingParams) { + /* USAGE: + $recordingParams = array( + 'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids + ); + */ + $recordingsUrl = $this->_bbbServerBaseUrl."api/getRecordings?"; + $params = 'meetingID='.urlencode($recordingParams['meetingId']); + return ($recordingsUrl.$params.'&checksum='.sha1("getRecordings".$params.$this->_securitySalt)); + + } + + public function getRecordingsWithXmlResponseArray($recordingParams) { + /* USAGE: + $recordingParams = array( + 'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids + ); + NOTE: 'duration' DOES work when creating a meeting, so if you set duration + when creating a meeting, it will kick users out after the duration. Should + probably be required in user code when 'recording' is set to true. + */ + $xml = $this->_processXmlResponse($this->getRecordingsUrl($recordingParams)); + if($xml) { + // If we don't get a success code or messageKey, find out why: + if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) { + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + return $result; + } + else { + // In this case, we have success and recording info: + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + $formats = []; + + foreach ($xml->recordings->recording as $r) { + foreach ($r->playback->format as $format) { + $formats[] = $format; + } + + $result[] = array( + 'recordId' => $r->recordID->__toString(), + 'meetingId' => $r->meetingID->__toString(), + 'name' => $r->name->__toString(), + 'published' => $r->published->__toString(), + 'startTime' => $r->startTime->__toString(), + 'endTime' => $r->endTime->__toString(), + 'playbackFormat' => $formats, + 'playbackFormatType' => $r->playback->format->type->__toString(), + 'playbackFormatUrl' => $r->playback->format->url->__toString(), + 'playbackFormatLength' => $r->playback->format->length->__toString(), + 'metadataTitle' => $r->metadata->title->__toString(), + 'metadataSubject' => $r->metadata->subject->__toString(), + 'metadataDescription' => $r->metadata->description->__toString(), + 'metadataCreator' => $r->metadata->creator->__toString(), + 'metadataContributor' => $r->metadata->contributor->__toString(), + 'metadataLanguage' => $r->metadata->language->__toString(), + // Add more here as needed for your app depending on your + // use of metadata when creating recordings. + ); + } + return $result; + } + } + else { + return null; + } + } + + /** + * @param $array recordingParams + * + * @return array|null + */ + public function getRecordings($recordingParams) + { + /* USAGE: + $recordingParams = array( + 'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids + ); + NOTE: 'duration' DOES work when creating a meeting, so if you set duration + when creating a meeting, it will kick users out after the duration. Should + probably be required in user code when 'recording' is set to true. + */ + $xml = $this->_processXmlResponse($this->getRecordingsUrl($recordingParams)); + if($xml) { + // If we don't get a success code or messageKey, find out why: + if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) { + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + return $result; + } + else { + // In this case, we have success and recording info: + $result = array( + 'returncode' => $xml->returncode->__toString(), + 'messageKey' => $xml->messageKey->__toString(), + 'message' => $xml->message->__toString() + ); + $result['records'] = []; + if (!empty($xml->recordings->recording)) { + $formats = []; + + foreach ($xml->recordings->recording as $r) { + foreach ($r->playback->format as $format) { + $formats[] = $format; + } + $result['records'][] = array( + 'recordId' => $r->recordID->__toString(), + 'meetingId' => $r->meetingID->__toString(), + 'name' => $r->name->__toString(), + 'published' => $r->published->__toString(), + 'startTime' => $r->startTime->__toString(), + 'endTime' => $r->endTime->__toString(), + 'playbackFormat' => $formats, + 'playbackFormatType' => $r->playback->format->type->__toString(), + 'playbackFormatUrl' => $r->playback->format->url->__toString(), + 'playbackFormatLength' => $r->playback->format->length->__toString(), + 'metadataTitle' => $r->metadata->title->__toString(), + 'metadataSubject' => $r->metadata->subject->__toString(), + 'metadataDescription' => $r->metadata->description->__toString(), + 'metadataCreator' => $r->metadata->creator->__toString(), + 'metadataContributor' => $r->metadata->contributor->__toString(), + 'metadataLanguage' => $r->metadata->language->__toString(), + ); + } + } + + return $result; + } + } + + return null; + } + + public function getPublishRecordingsUrl($recordingParams) { + /* USAGE: + $recordingParams = array( + 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids + 'publish' => 'true', -- REQUIRED - boolean: true/false + ); + */ + $recordingsUrl = $this->_bbbServerBaseUrl."api/publishRecordings?"; + $params = + 'recordID='.urlencode($recordingParams['recordId']). + '&publish='.urlencode($recordingParams['publish']); + return ($recordingsUrl.$params.'&checksum='.sha1("publishRecordings".$params.$this->_securitySalt)); + + } + + public function publishRecordingsWithXmlResponseArray($recordingParams) { + /* USAGE: + $recordingParams = array( + 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids + 'publish' => 'true', -- REQUIRED - boolean: true/false + ); + */ + $xml = $this->_processXmlResponse($this->getPublishRecordingsUrl($recordingParams)); + if($xml) { + return array( + 'returncode' => $xml->returncode->__toString(), + 'published' => $xml->published->__toString() // -- Returns true/false. + ); + } + else { + return null; + } + + + } + + public function getDeleteRecordingsUrl($recordingParams) { + /* USAGE: + $recordingParams = array( + 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids + ); + */ + $recordingsUrl = $this->_bbbServerBaseUrl."api/deleteRecordings?"; + $params = + 'recordID='.urlencode($recordingParams['recordId']); + return ($recordingsUrl.$params.'&checksum='.sha1("deleteRecordings".$params.$this->_securitySalt)); + } + + public function deleteRecordingsWithXmlResponseArray($recordingParams) { + /* USAGE: + $recordingParams = array( + 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids + ); + */ + + $xml = $this->_processXmlResponse($this->getDeleteRecordingsUrl($recordingParams)); + if($xml) { + return array( + 'returncode' => $xml->returncode->__toString(), + 'deleted' => $xml->deleted->__toString() // -- Returns true/false. + ); + } + else { + return null; + } + + } + + /** USAGE: + * $recordingParams = array( + * 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids + * ); + */ + public function generateRecording($recordingParams) + { + if (empty($recordingParams)) { + return false; + } + + $recordingsUrl = $this->_bbbServerBaseUrl.'../demo/regenerateRecord.jsp?'; + $params = 'recordID='.urlencode($recordingParams['recordId']); + $url = $recordingsUrl.$params.'&checksum='.sha1('regenerateRecord'.$params.$this->_securitySalt); + + $ch = curl_init() or die ( curl_error($ch) ); + $timeout = 10; + curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt( $ch, CURLOPT_URL, $url ); + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); + curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout); + $data = curl_exec( $ch ); + curl_close( $ch ); + + return true; + } +} diff --git a/plugin/bbb/lib/bbb_plugin.class.php b/plugin/bbb/lib/bbb_plugin.class.php new file mode 100644 index 000000000..4141eb1e6 --- /dev/null +++ b/plugin/bbb/lib/bbb_plugin.class.php @@ -0,0 +1,407 @@ + 'big_blue_button_record_and_store', + 'type' => 'checkbox', + ], + [ + 'name' => 'bbb_enable_conference_in_groups', + 'type' => 'checkbox', + ], + [ + 'name' => 'bbb_force_record_generation', + 'type' => 'checkbox', + ], + [ + 'name' => 'big_blue_button_students_start_conference_in_groups', + 'type' => 'checkbox', + ], + ]; + + /** + * BBBPlugin constructor. + */ + protected function __construct() + { + $settings = [ + 'tool_enable' => 'boolean', + 'host' => 'text', + 'salt' => 'text', + 'enable_global_conference' => 'boolean', + 'enable_global_conference_per_user' => 'boolean', + 'enable_conference_in_course_groups' => 'boolean', + 'enable_global_conference_link' => 'boolean', + 'disable_download_conference_link' => 'boolean', + 'max_users_limit' => 'text', + 'global_conference_allow_roles' => [ + 'type' => 'select', + 'options' => [ + PLATFORM_ADMIN => get_lang('Administrator'), + COURSEMANAGER => get_lang('Teacher'), + STUDENT => get_lang('Student'), + STUDENT_BOSS => get_lang('StudentBoss'), + ], + 'attributes' => ['multiple' => 'multiple'], + ], + 'allow_regenerate_recording' => 'boolean', + // Default course settings, must be the same as $course_settings + 'big_blue_button_record_and_store' => 'checkbox', + 'bbb_enable_conference_in_groups' => 'checkbox', + 'bbb_force_record_generation' => 'checkbox', + 'disable_course_settings' => 'boolean', + 'meeting_duration' => 'text', + ]; + + if (1 === (int) api_get_current_access_url_id()) { + $settings['plugin_bbb_multiple_urls_cron_apply_to_all'] = 'checkbox'; + } + + parent::__construct( + '2.11', + 'Julio Montoya, Yannick Warnier, Angel Fernando Quiroz Campos, Jose Angel Ruiz, Ghazi Triki, Adnen Manssouri', + $settings + ); + + $this->isAdminPlugin = true; + } + + /** + * @return BBBPlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @param string $variable + * + * @return bool + */ + public function validateCourseSetting($variable) + { + if ($this->get('disable_course_settings') === 'true') { + return false; + } + + $result = true; + switch ($variable) { + case 'bbb_enable_conference_in_groups': + $result = $this->get('enable_conference_in_course_groups') === 'true'; + break; + case 'bbb_force_record_generation': + $result = $this->get('allow_regenerate_recording') === 'true'; + break; + case 'big_blue_button_record_and_store': + } + + return $result; + } + + /** + * + * @return array + */ + public function getCourseSettings() + { + $settings = []; + if ($this->get('disable_course_settings') !== 'true') { + $settings = parent::getCourseSettings(); + } + + return $settings; + } + + /** + * + * @return \Plugin + */ + public function performActionsAfterConfigure() + { + $result = $this->get('disable_course_settings') === 'true'; + if ($result) { + $valueConference = $this->get('bbb_enable_conference_in_groups') === 'true' ? 1 : 0; + self::update_course_field_in_all_courses('bbb_enable_conference_in_groups', $valueConference); + + $valueForceRecordGeneration = $this->get('bbb_force_record_generation') === 'true' ? 1 : 0; + self::update_course_field_in_all_courses('bbb_force_record_generation', $valueForceRecordGeneration); + + $valueForceRecordStore = $this->get('big_blue_button_record_and_store') === 'true' ? 1 : 0; + self::update_course_field_in_all_courses('big_blue_button_record_and_store', $valueForceRecordStore); + } + + return $this; + } + + /** + * Install + */ + public function install() + { + $sql = "CREATE TABLE IF NOT EXISTS plugin_bbb_meeting ( + id INT unsigned NOT NULL auto_increment PRIMARY KEY, + c_id INT unsigned NOT NULL DEFAULT 0, + group_id INT unsigned NOT NULL DEFAULT 0, + user_id INT unsigned NOT NULL DEFAULT 0, + meeting_name VARCHAR(255) NOT NULL DEFAULT '', + attendee_pw VARCHAR(255) NOT NULL DEFAULT '', + moderator_pw VARCHAR(255) NOT NULL DEFAULT '', + record INT NOT NULL DEFAULT 0, + status INT NOT NULL DEFAULT 0, + created_at VARCHAR(255) NOT NULL, + closed_at VARCHAR(255) NOT NULL, + calendar_id INT DEFAULT 0, + welcome_msg VARCHAR(255) NOT NULL DEFAULT '', + session_id INT unsigned DEFAULT 0, + remote_id CHAR(30), + internal_meeting_id VARCHAR(255) DEFAULT NULL, + visibility TINYINT NOT NULL DEFAULT 1, + voice_bridge INT NOT NULL DEFAULT 1, + access_url INT NOT NULL DEFAULT 1, + video_url TEXT NULL, + has_video_m4v TINYINT NOT NULL DEFAULT 0 + )"; + Database::query($sql); + + Database::query( + "CREATE TABLE IF NOT EXISTS plugin_bbb_room ( + id int NOT NULL AUTO_INCREMENT PRIMARY KEY, + meeting_id int NOT NULL, + participant_id int(11) NOT NULL, + in_at datetime, + out_at datetime, + close INT NOT NULL DEFAULT 0 + );" + ); + $fieldLabel = 'plugin_bbb_course_users_limit'; + $fieldType = ExtraField::FIELD_TYPE_INTEGER; + $fieldTitle = $this->get_lang('MaxUsersInConferenceRoom'); + $fieldDefault = '0'; + $extraField = new ExtraField('course'); + $fieldId = CourseManager::create_course_extra_field( + $fieldLabel, + $fieldType, + $fieldTitle, + $fieldDefault + ); + $extraField->find($fieldId); + $extraField->update( + [ + 'id' => $fieldId, + 'variable' => 'plugin_bbb_course_users_limit', + 'changeable' => 1, + 'visible_to_self' => 1, + 'visible_to_others' => 0, + ] + ); + $fieldLabel = 'plugin_bbb_session_users_limit'; + $extraField = new ExtraField('session'); + $fieldId = SessionManager::create_session_extra_field( + $fieldLabel, + $fieldType, + $fieldTitle, + $fieldDefault + ); + $extraField->find($fieldId); + $extraField->update( + [ + 'id' => $fieldId, + 'variable' => 'plugin_bbb_session_users_limit', + 'changeable' => 1, + 'visible_to_self' => 1, + 'visible_to_others' => 0, + ] + ); + + Database::query( + "CREATE TABLE IF NOT EXISTS plugin_bbb_meeting_format ( + id int unsigned not null PRIMARY KEY AUTO_INCREMENT, + meeting_id int unsigned not null, + format_type varchar(255) not null, + resource_url text not null + );" + ); + + // Copy icons into the main/img/icons folder + $iconName = 'bigbluebutton'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $sourceDir = api_get_path(SYS_PLUGIN_PATH).'bbb/resources/img/'; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $src = $sourceDir.$icon; + $dest = $destinationDir.$icon; + copy($src, $dest); + } + // Installing course settings + $this->install_course_fields_in_all_courses(true); + } + + /** + * Uninstall + */ + public function uninstall() + { + $t_settings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); + $t_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS); + $t_tool = Database::get_course_table(TABLE_TOOL_LIST); + + $variables = [ + 'bbb_salt', + 'bbb_host', + 'bbb_tool_enable', + 'enable_global_conference', + 'enable_global_conference_per_user', + 'enable_global_conference_link', + 'disable_download_conference_link', + 'enable_conference_in_course_groups', + 'bbb_plugin', + 'bbb_plugin_host', + 'bbb_plugin_salt', + 'max_users_limit', + 'global_conference_allow_roles' + ]; + + $urlId = api_get_current_access_url_id(); + + foreach ($variables as $variable) { + $sql = "DELETE FROM $t_settings WHERE variable = '$variable' AND access_url = $urlId"; + Database::query($sql); + } + + $em = Database::getManager(); + $sm = $em->getConnection()->getSchemaManager(); + if ($sm->tablesExist('plugin_bbb_meeting')) { + Database::query("DELETE FROM plugin_bbb_meeting WHERE access_url = $urlId"); + } + + // Only delete tables if it's uninstalled from main url. + if (1 == $urlId) { + $extraField = new ExtraField('course'); + $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable( + 'plugin_bbb_course_users_limit' + ); + if (!empty($extraFieldInfo)) { + $extraField->delete($extraFieldInfo['id']); + } + $extraField = new ExtraField('session'); + $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable( + 'plugin_bbb_session_users_limit' + ); + if (!empty($extraFieldInfo)) { + $extraField->delete($extraFieldInfo['id']); + } + + $sql = "DELETE FROM $t_options WHERE variable = 'bbb_plugin'"; + Database::query($sql); + + // hack to get rid of Database::query warning (please add c_id...) + $sql = "DELETE FROM $t_tool WHERE name = 'bbb' AND c_id != 0"; + Database::query($sql); + + if ($sm->tablesExist('plugin_bbb_meeting_format')) { + Database::query('DROP TABLE IF EXISTS plugin_bbb_meeting_format'); + } + if ($sm->tablesExist('plugin_bbb_room')) { + Database::query('DROP TABLE IF EXISTS plugin_bbb_room'); + } + if ($sm->tablesExist('plugin_bbb_meeting')) { + Database::query('DROP TABLE IF EXISTS plugin_bbb_meeting'); + } + + // Deleting course settings + $this->uninstall_course_fields_in_all_courses($this->course_settings); + + // Remove icons from the main/img/icons folder + $iconName = 'bigbluebutton'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $dest = $destinationDir.$icon; + if (is_file($dest)) { + @unlink($dest); + } + } + } + } + + /** + * Update + */ + public function update() + { + $sql = "SHOW COLUMNS FROM plugin_bbb_room WHERE Field = 'close'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE plugin_bbb_room ADD close int unsigned NULL"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + + Database::update( + 'plugin_bbb_room', + ['close' => BBBPlugin::ROOM_CLOSE] + ); + } + } + + /** + * Set the course setting in all courses + * + * @param bool $variable Course setting to update + * @param bool $value New values of the course setting + */ + public function update_course_field_in_all_courses($variable, $value) + { + // Update existing courses to add the new course setting value + $table = Database::get_main_table(TABLE_MAIN_COURSE); + $sql = "SELECT id FROM $table ORDER BY id"; + $res = Database::query($sql); + $courseSettingTable = Database::get_course_table(TABLE_COURSE_SETTING); + while ($row = Database::fetch_assoc($res)) { + Database::update( + $courseSettingTable, + ['value' => $value], + ['variable = ? AND c_id = ?' => [$variable, $row['id']]] + ); + } + } +} diff --git a/plugin/bbb/lib/vm/AbstractVM.php b/plugin/bbb/lib/vm/AbstractVM.php new file mode 100644 index 000000000..4c9527f68 --- /dev/null +++ b/plugin/bbb/lib/vm/AbstractVM.php @@ -0,0 +1,50 @@ +name = $settings['name']; + $this->host = $settings['host']; + $this->user = $settings['user']; + $this->apiKey = $settings['api_key']; + $this->vmId = $settings['vm_id']; + $this->vmMinSize = $settings['vm_min_size_id']; + $this->vmMaxSize = $settings['vm_max_size_id']; + $this->vmClientId = $settings['vm_client_id']; + } + + /** + * @param string $message + */ + public function addMessage($message) + { + $this->messages[] = $message; + } + + /** + * @return string + */ + public function getMessageToString() + { + return implode(PHP_EOL, $this->messages); + } +} diff --git a/plugin/bbb/lib/vm/AmazonVM.php b/plugin/bbb/lib/vm/AmazonVM.php new file mode 100644 index 000000000..683cc15b5 --- /dev/null +++ b/plugin/bbb/lib/vm/AmazonVM.php @@ -0,0 +1,44 @@ +connect(); + } + + /** + * @inheritdoc + */ + public function connect() + { + // Set up your credentials. + $credentials = new Credentials($this->vmClientId, $this->apiKey); + // Use the default adapter, CurlHttpAdapter. + $this->connector = new DigitalOcean($credentials); + + // Or use BuzzHttpAdapter. + //$this->connector = new DigitalOcean($credentials, new BuzzHttpAdapter()); + } + + /** + * @return DigitalOcean + */ + public function getConnector() + { + return $this->connector; + } + + /** + * @param string $type min or max + */ + public function resizeTo($type = 'min') + { + try { + $droplets = $this->getConnector()->droplets(); + $sizes = $this->getConnector()->sizes(); + $availableSizes = $sizes->getAll(); + + if (isset($availableSizes->status) && $availableSizes->status == 'OK') { + + $minSizeIdExists = false; + $maxSizeIdExists = false; + + foreach ($availableSizes->sizes as $size) { + if ($size->id == $this->vmMaxSize) { + $maxSizeIdExists = true; + } + if ($size->id == $this->vmMinSizeSize) { + $minSizeIdExists = true; + } + } + if ($maxSizeIdExists && $minSizeIdExists) { + throw new \Exception('Sizes are not well configured'); + } + } else { + throw new \Exception('Sizes not available'); + } + + // Returns all active droplets that are currently running in your account. + //$allActive = $droplets->showAllActive(); + + $dropletInfo = $droplets->show($this->vmId); + + if ($dropletInfo->status == 'OK') { + switch ($type) { + case 'min': + if ($dropletInfo->droplet->size_id == $this->vmMinSize) { + // No resize + $this->addMessage( + 'Nothing to execute. The size was already reduced.' + ); + } else { + $this->resize($this->vmMinSize); + } + + break; + case 'max': + if ($dropletInfo->droplet->size_id == $this->vmMaxSize) { + // No resize + $this->addMessage( + 'Nothing to execute. The size was already boost.' + ); + } else { + $this->resize($this->vmMaxSize); + } + break; + } + } else { + throw new \Exception(" Id ".$this->vmId." doesn't exists."); + } + } catch (Exception $e) { + die($e->getMessage()); + } + } + + /** + * Turns off / resize / turns on + * @param int $sizeId + */ + public function resize($sizeId) + { + $droplets = $this->getConnector()->droplets(); + $dropletInfo = $droplets->show($this->vmId); + + $powerOff = $droplets->powerOff($this->vmId); + + $this->addMessage('Power off droplet #'.$this->vmId); + + $this->waitForEvent($powerOff->event_id); + + $this->addMessage('Current status: '.$dropletInfo->droplet->status); + + $resizeDroplet = $droplets->resize( + $this->vmId, + array('size_id' => intval($sizeId)) + ); + $this->addMessage('Resize droplet to size id: '.$sizeId); + $this->waitForEvent($resizeDroplet->event_id); + + $powerOn = $droplets->powerOn($this->vmId); + $this->waitForEvent($powerOn->event_id); + $this->addMessage('Power on droplet #'.$this->vmId); + + } + + /** + * Loops until an event answer 100 percentage + * @param int $eventId + */ + public function waitForEvent($eventId) + { + $events = $this->getConnector()->events(); + $status = false; + while ($status == false) { + $infoStatus = $events->show($eventId); + if ($infoStatus->status == 'OK' && $infoStatus->event->percentage == 100) { + $status = true; + } + } + } + + /** + * @inheritdoc + */ + public function runCron() + { + $this->resizeToMinLimit(); + echo $this->getMessageToString(); + } + + /** + * @inheritdoc + */ + public function resizeToMaxLimit() + { + $this->resizeTo('max'); + } + + /** + * @inheritdoc + */ + public function resizeToMinLimit() + { + $this->resizeTo('min'); + } +} diff --git a/plugin/bbb/lib/vm/VMInterface.php b/plugin/bbb/lib/vm/VMInterface.php new file mode 100644 index 000000000..8019a3668 --- /dev/null +++ b/plugin/bbb/lib/vm/VMInterface.php @@ -0,0 +1,29 @@ +get_lang('Videoconference'); +$roomTable = Database::get_main_table('plugin_bbb_room'); + +$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'bbb/resources/utils.js'); + +$action = $_GET['action'] ?? ''; +$userId = api_get_user_id(); +$groupId = api_get_group_id(); +$sessionId = api_get_session_id(); +$courseInfo = api_get_course_info(); + +$bbb = new bbb('', '', $isGlobal, $isGlobalPerUser); + +$conferenceManager = $bbb->isConferenceManager(); +if ($bbb->isGlobalConference()) { + api_block_anonymous_users(); +} else { + api_protect_course_script(true); +} + +$allowStudentAsConferenceManager = false; +if (!empty($courseInfo) && !empty($groupId) && !api_is_allowed_to_edit()) { + $groupEnabled = api_get_course_plugin_setting( + 'bbb', + 'bbb_enable_conference_in_groups', + $courseInfo + ) === '1'; + if ($groupEnabled) { + $isSubscribed = GroupManager::is_user_in_group(api_get_user_id(), GroupManager::get_group_properties($groupId)); + if ($isSubscribed) { + $allowStudentAsConferenceManager = api_get_course_plugin_setting( + 'bbb', + 'big_blue_button_students_start_conference_in_groups', + $courseInfo + ) === '1'; + } + } +} + +$allowToEdit = $conferenceManager; +// Disable students edit permissions. +if ($allowStudentAsConferenceManager) { + $allowToEdit = false; +} + +$courseCode = $courseInfo['code'] ?? ''; + +$message = ''; +if ($conferenceManager && $allowToEdit) { + switch ($action) { + case 'add_to_calendar': + if ($bbb->isGlobalConference()) { + return false; + } + $courseInfo = api_get_course_info(); + $agenda = new Agenda('course'); + $id = (int) $_GET['id']; + $title = sprintf($plugin->get_lang('VideoConferenceXCourseX'), $id, $courseInfo['name']); + $content = Display::url($plugin->get_lang('GoToTheVideoConference'), $_GET['url']); + + $eventId = $agenda->addEvent( + $_REQUEST['start'], + null, + 'true', + $title, + $content, + ['everyone'] + ); + if (!empty($eventId)) { + $message = Display::return_message($plugin->get_lang('VideoConferenceAddedToTheCalendar'), 'success'); + } else { + $message = Display::return_message(get_lang('Error'), 'error'); + } + break; + case 'copy_record_to_link_tool': + $result = $bbb->copyRecordingToLinkTool($_GET['id']); + if ($result) { + $message = Display::return_message($plugin->get_lang('VideoConferenceAddedToTheLinkTool'), 'success'); + } else { + $message = Display::return_message(get_lang('Error'), 'error'); + } + break; + case 'regenerate_record': + if ($plugin->get('allow_regenerate_recording') !== 'true') { + api_not_allowed(true); + } + $recordId = isset($_GET['record_id']) ? $_GET['record_id'] : ''; + $result = $bbb->regenerateRecording($_GET['id'], $recordId); + if ($result) { + $message = Display::return_message(get_lang('Success'), 'success'); + } else { + $message = Display::return_message(get_lang('Error'), 'error'); + } + + Display::addFlash($message); + header('Location: '.$bbb->getListingUrl()); + exit; + break; + case 'delete_record': + $result = $bbb->deleteRecording($_GET['id']); + if ($result) { + $message = Display::return_message(get_lang('Deleted'), 'success'); + } else { + $message = Display::return_message(get_lang('Error'), 'error'); + } + + Display::addFlash($message); + header('Location: '.$bbb->getListingUrl()); + exit; + break; + case 'end': + $bbb->endMeeting($_GET['id']); + $message = Display::return_message( + $plugin->get_lang('MeetingClosed').'
'.$plugin->get_lang('MeetingClosedComment'), + 'success', + false + ); + + if (file_exists(__DIR__.'/config.vm.php')) { + require __DIR__.'/../../vendor/autoload.php'; + require __DIR__.'/lib/vm/AbstractVM.php'; + require __DIR__.'/lib/vm/VMInterface.php'; + require __DIR__.'/lib/vm/DigitalOceanVM.php'; + require __DIR__.'/lib/VM.php'; + $config = require __DIR__.'/config.vm.php'; + + $vm = new VM($config); + $vm->resizeToMinLimit(); + } + + Display::addFlash($message); + header('Location: '.$bbb->getListingUrl()); + exit; + break; + case 'publish': + $bbb->publishMeeting($_GET['id']); + Display::addFlash(Display::return_message(get_lang('Updated'))); + header('Location: '.$bbb->getListingUrl()); + exit; + break; + case 'unpublish': + $bbb->unpublishMeeting($_GET['id']); + Display::addFlash(Display::return_message(get_lang('Updated'))); + header('Location: '.$bbb->getListingUrl()); + exit; + break; + case 'logout': + if ($plugin->get('allow_regenerate_recording') === 'true') { + $setting = api_get_course_plugin_setting('bbb', 'bbb_force_record_generation', $courseInfo); + $allow = $setting == 1 ? true : false; + if ($allow) { + $result = $bbb->getMeetingByRemoteId($_GET['remote_id']); + if (!empty($result)) { + $result = $bbb->regenerateRecording($result['id']); + if ($result) { + Display::addFlash(Display::return_message(get_lang('Success'))); + } else { + Display::addFlash(Display::return_message(get_lang('Error'), 'error')); + } + } + } + } + + $remoteId = Database::escape_string($_GET['remote_id']); + $meetingData = Database::select( + '*', + Database::get_main_table('plugin_bbb_meeting'), + ['where' => ['remote_id = ? AND access_url = ?' => [$remoteId, api_get_current_access_url_id()]]], + 'first' + ); + + if (empty($meetingData) || !is_array($meetingData)) { + error_log("meeting does not exist - remote_id: $remoteId"); + } else { + $meetingId = $meetingData['id']; + + // If creator -> update + if ($meetingData['creator_id'] == api_get_user_id()) { + $pass = $bbb->getModMeetingPassword($courseCode); + + $meetingBBB = $bbb->getMeetingInfo( + [ + 'meetingId' => $remoteId, + 'password' => $pass, + ] + ); + + if ($meetingBBB === false) { + //checking with the remote_id didn't work, so just in case and + // to provide backwards support, check with the id + $params = [ + 'meetingId' => $meetingId, + // -- REQUIRED - The unique id for the meeting + 'password' => $pass, + // -- REQUIRED - The moderator password for the meeting + ]; + $meetingBBB = $bbb->getMeetingInfo($params); + } + + if (!empty($meetingBBB)) { + if (isset($meetingBBB['returncode'])) { + $status = (string) $meetingBBB['returncode']; + switch ($status) { + case 'FAILED': + $bbb->endMeeting($meetingId, $courseCode); + break; + case 'SUCCESS': + $i = 0; + while ($i < $meetingBBB['participantCount']) { + $participantId = $meetingBBB[$i]['userId']; + $roomData = Database::select( + '*', + $roomTable, + [ + 'where' => [ + 'meeting_id = ? AND participant_id = ? AND close = ?' => [ + $meetingId, + $participantId, + BBBPlugin::ROOM_OPEN, + ], + ], + 'order' => 'id DESC', + ], + 'first' + ); + + if (!empty($roomData)) { + $roomId = $roomData['id']; + if (!empty($roomId)) { + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime()], + ['id = ? ' => $roomId] + ); + } + } + $i++; + } + break; + } + } + } + } + + // Update out_at field of user + $roomData = Database::select( + '*', + $roomTable, + [ + 'where' => ['meeting_id = ? AND participant_id = ?' => [$meetingId, $userId]], + 'order' => 'id DESC', + ], + 'first' + ); + + if (!empty($roomData)) { + $roomId = $roomData['id']; + if (!empty($roomId)) { + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE], + ['id = ? ' => $roomId] + ); + } + } + + $message = Display::return_message( + $plugin->get_lang('RoomClosed').'
'.$plugin->get_lang('RoomClosedComment'), + 'success', + false + ); + Display::addFlash($message); + } + + header('Location: '.$bbb->getListingUrl()); + exit; + break; + default: + break; + } +} else { + if ($action === 'logout') { + // Update out_at field of user + $remoteId = Database::escape_string($_GET['remote_id']); + $meetingData = Database::select( + '*', + Database::get_main_table('plugin_bbb_meeting'), + ['where' => ['remote_id = ? AND access_url = ?' => [$remoteId, api_get_current_access_url_id()]]], + 'first' + ); + + if (empty($meetingData) || !is_array($meetingData)) { + error_log("meeting does not exist - remote_id: $remoteId"); + } else { + $meetingId = $meetingData['id']; + $roomData = Database::select( + '*', + $roomTable, + [ + 'where' => [ + 'meeting_id = ? AND participant_id = ? AND close = ?' => [ + $meetingId, + $userId, + BBBPlugin::ROOM_OPEN, + ], + ], + 'order' => 'id DESC', + ] + ); + + $i = 0; + foreach ($roomData as $item) { + $roomId = $item['id']; + if (!empty($roomId)) { + if ($i == 0) { + Database::update( + $roomTable, + ['out_at' => api_get_utc_datetime(), 'close' => BBBPlugin::ROOM_CLOSE], + ['id = ? ' => $roomId] + ); + } else { + Database::update($roomTable, ['close' => BBBPlugin::ROOM_CLOSE], ['id = ? ' => $roomId]); + } + $i++; + } + } + + $message = Display::return_message( + $plugin->get_lang('RoomExit'), + 'success', + false + ); + Display::addFlash($message); + } + header('Location: '.$bbb->getListingUrl()); + exit; + } +} + +if (isset($_GET['page_id'])) { + $pageId = (int) $_GET['page_id']; +} + +$meetingsCount = $bbb->getCountMeetings( + api_get_course_int_id(), + api_get_session_id(), + api_get_group_id() +); + +$limit = 10; +$pageNumber = ceil($meetingsCount / $limit); + +if (!isset($pageId)) { + $pageId = 1; +} + +$start = ($pageId - 1) * $limit; + +$meetings = $bbb->getMeetings( + api_get_course_int_id(), + api_get_session_id(), + api_get_group_id(), + false, + [], + $start, + $limit, + "DESC" +); + +if (empty($meetings)) { + $pageId = 0; +} + +$usersOnline = $bbb->getUsersOnlineInCurrentRoom(); +$maxUsers = $bbb->getMaxUsersLimit(); +$status = $bbb->isServerRunning(); +$currentOpenConference = $bbb->getCurrentVideoConference(); +$videoConferenceName = $currentOpenConference + ? $currentOpenConference['meeting_name'] + : $bbb->generateVideoConferenceName(); +$meetingExists = $bbb->meetingExists($videoConferenceName); +$showJoinButton = false; + +// Only conference manager can see the join button +$userCanSeeJoinButton = $conferenceManager; +if ($bbb->isGlobalConference() && $bbb->isGlobalConferencePerUserEnabled()) { + // Any user can see the "join button" BT#12620 + $userCanSeeJoinButton = true; +} + +if (($meetingExists || $userCanSeeJoinButton) && ($maxUsers == 0 || $maxUsers > $usersOnline)) { + $showJoinButton = true; +} +$conferenceUrl = $bbb->getConferenceUrl(); +$courseInfo = api_get_course_info(); +$formToString = ''; + +if ($bbb->isGlobalConference() === false && + !empty($courseInfo) && + $plugin->get('enable_conference_in_course_groups') === 'true' +) { + $url = api_get_self().'?'.api_get_cidreq(true, false).'&gidReq='; + $htmlHeadXtra[] = ''; + + $form = new FormValidator(api_get_self().'?'.api_get_cidreq()); + if ($conferenceManager && false === $allowStudentAsConferenceManager) { + $groups = GroupManager::get_group_list(null, $courseInfo, null, $sessionId); + } else { + if (!empty($groupId)) { + $groupInfo = GroupManager::get_group_properties($groupId); + if ($groupInfo) { + $isSubscribed = GroupManager::is_user_in_group(api_get_user_id(), $groupInfo); + if (false === $isSubscribed) { + api_not_allowed(true); + } + } + } + + $groups = GroupManager::getAllGroupPerUserSubscription( + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + } + + if ($groups) { + $meetingsInGroup = $bbb->getAllMeetingsInCourse(api_get_course_int_id(), api_get_session_id(), 1); + $meetingsGroup = array_column($meetingsInGroup, 'status', 'group_id'); + $groupList[0] = get_lang('Select'); + foreach ($groups as $groupData) { + $itemGroupId = $groupData['iid']; + if (isset($meetingsGroup[$itemGroupId]) && $meetingsGroup[$itemGroupId] == 1) { + $groupData['name'] .= ' ('.get_lang('Active').')'; + } + $groupList[$itemGroupId] = $groupData['name']; + } + + $form->addSelect('group_id', get_lang('Groups'), $groupList, ['id' => 'group_select']); + $form->setDefaults(['group_id' => $groupId]); + $formToString = $form->returnForm(); + } +} + +$frmEnterConference = new FormValidator( + 'enter_conference', + 'get', + api_get_path(WEB_PLUGIN_PATH).'bbb/start.php', + '_blank' +); +$frmEnterConference->addText('name', get_lang('Name')); +$frmEnterConference->applyFilter('name', 'trim'); +$frmEnterConference->addButtonNext($plugin->get_lang('EnterConference')); + +$conferenceUrlQueryParams = []; + +parse_str( + parse_url($conferenceUrl, PHP_URL_QUERY), + $conferenceUrlQueryParams +); + +foreach ($conferenceUrlQueryParams as $key => $value) { + $frmEnterConference->addHidden($key, $value); +} + +if ($meetingExists) { + $meetingInfo = $bbb->getMeetingByName($videoConferenceName); + + if (1 === (int) $meetingInfo['status']) { + $frmEnterConference->freeze(['name']); + } +} + +$frmEnterConference->setDefaults(['name' => $videoConferenceName]); + +// Default URL +$enterConferenceLink = $frmEnterConference->returnForm(); + +$tpl = new Template($tool_name); + +$tpl->assign('allow_to_edit', $allowToEdit); +$tpl->assign('meetings', $meetings); +$tpl->assign('conference_url', $conferenceUrl); +$tpl->assign('users_online', $usersOnline); +$tpl->assign('conference_manager', $conferenceManager); +$tpl->assign('max_users_limit', $maxUsers); +$tpl->assign('bbb_status', $status); +$tpl->assign('show_join_button', $showJoinButton); +$tpl->assign('message', $message); +$tpl->assign('form', $formToString); +$tpl->assign('enter_conference_links', $enterConferenceLink); +$tpl->assign('page_number', $pageNumber); +$tpl->assign('page_id', $pageId); + +$content = $tpl->fetch('bbb/view/listing.tpl'); + +$actionLinks = ''; +if (api_is_platform_admin()) { + $actionLinks .= Display::toolbarButton( + $plugin->get_lang('AdminView'), + api_get_path(WEB_PLUGIN_PATH).'bbb/admin.php', + 'list', + 'primary' + ); + + $tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) + ); +} + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/bbb/plugin.php b/plugin/bbb/plugin.php new file mode 100644 index 000000000..0f88af8f3 --- /dev/null +++ b/plugin/bbb/plugin.php @@ -0,0 +1,6 @@ +get_info(); diff --git a/plugin/bbb/resources/img/128/bigbluebutton.png b/plugin/bbb/resources/img/128/bigbluebutton.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9a573bb80c709efecd92f825c091698fe7efb7 GIT binary patch literal 3380 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 KMNUMnLSTXw%}=NR literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/128/bigbluebutton_na.png b/plugin/bbb/resources/img/128/bigbluebutton_na.png new file mode 100644 index 0000000000000000000000000000000000000000..9953b99505ffacd8489a1d8d58bfb343f84a3d9b GIT binary patch literal 3671 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 literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/128/videoconference_flash.png b/plugin/bbb/resources/img/128/videoconference_flash.png new file mode 100644 index 0000000000000000000000000000000000000000..e6d9f4e7ca8fc8eced7261e24f822aa5264dd5cc GIT binary patch literal 6506 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 literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/128/videoconference_html5.png b/plugin/bbb/resources/img/128/videoconference_html5.png new file mode 100644 index 0000000000000000000000000000000000000000..e3cdeffe4a4e34f4ded1873b49834c94642ff98d GIT binary patch literal 7475 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 Ut4Zmn7lIR@s;H?@CubS{Kkse3)Bpeg literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/22/bigbluebutton.png b/plugin/bbb/resources/img/22/bigbluebutton.png new file mode 100644 index 0000000000000000000000000000000000000000..3b2fdf86b8beee6df9af1a2a6365260307db498e GIT binary patch literal 740 zcmV0007>0k+TsL;wH)8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10%1u+K~y-6-IUEsRACf{pL6ewO^QWDl1VyYsTC18Hhqv#AkjuB zNQq|CzaVK)ZN$Q|45HcuZYu~{)S`_+i$W{ZF+tE8%@8$akY*}%t~2+(En+^Lxz5Byfj(HNLm5Hz()GVgz9~<8A4%)5123OU#)Qjkyf*iBE zSEy-G>iXDFagSX9TX1!4>s1ly0JMBBQt;9+W4@{3yWyPR8c*wGkj{L-m2maej2I1p zz>E$*XKziZZHBumOL+Fe&!IXu zaZ~Z==?8#9r-L)VLrD7^%Tz~5FO8_2&-XQZ@HKl;Ry=v8AlX5Vl&Ne_cMOLOJ_SOn zf;Vs4$mfX=Q6u&Ti6D|ZCxTs!>5W+ck$2ue4~r1=Xu8?ZLSJ)_|S+nud-0c)M%e;;&L&>4k=2h7$AO|I%-2 Wn%i#WAR8V400008FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10)t6JK~y-6-IPyD8&MFzXWqUsF}pLpxXYSsv_00j&A;E9 z8RiYZ|0Z}_K%r0=kWxNoW)%Pui1?d`ei?>w008{Y;K|9!F+?mYiZU88bCAQ#77@KO z4C71O1F_&@vA7_FcmV*6h&`)fWA4VM2@whL=1CzUBVv}B z$MX67w_2@6flovOCnhE|L|j5d>c;ejD~dAWIL<=UE~82!k$4%jK0iPIXmN2-mr{0h zb$WVwwz|5y)oQhTP1Dk~TFpkpr}=#T!~XuheGNP{HI*TvT%ga*%{>_$9DEp8Wj31~ z7#|LL~b8`wIZ@`PwLa-yao1ghM4F5yK3nTrN+~&dwG+&kKu_ zQjT2zB5_4ReFn`K#-zVBaqm5AD|>%OX1 zs}4ZN9w(E@_e7)v!2PSrJlnRt*s{B2D}VxY{Jib$?UoSYH4(M`5(WSO%=|NzN^L}K zU2WFubz9T49nbS}LWoSKzf*|ltLwUNE0sz+YVUbvR#sNT=H_O}_x))^97DvgJ0dz` z<{!-b$uNw=xcplWJph2N>j_0sGQRI89mjEkdvLep4@XTDXXKs9MgRZ+07*qoM6N<$ EfgBnz7Af!l%4<*{Xe zypkO$wxD|RH^YC5s3IeKx#yFw<&EAFiTcyNEBipSbWFm-Qb~x$S<7lB#u@rT;r!Zc z0@cOVQd4YLv&wFvzEF9YSV1r0#KqPj@{7ROCx%lQCHY}>pd8ZKo(Yb&Gy&}>19s^&2# zcRfe6tat_#CFXmvmYx}Th5(_E%oZa4WcogQ#qfxP^`!^{NEwQA2$W+807V_p69a%_ zb{_JlXIY9K8X7@rsvQ6j3duO134`&d5d{K038#)?QyzJ`aO%wMpO5NRlo)-60G&nM zJ^+6G&;*NOB7uvSAK>hTd*MLR0TB0SyJOYV6e7kA{m7TEyDi7RcoptF+vr9^qdU%L zi8~q~eZ$wB z+aRnqO-n|EL*BOI`(sN9Uq&FY6Tr7Ij`y*hj_8XC0lc20FF@FY(cOV&3KDp8$HQX- zdc*J!iNCk`WmfKr&j2coKrM=ysmlBG_|-(_FHg9mcy9$WUj{HaiSQm0ys`bsi91FO zhAR+}zxUzWiMgc>0tgV;36S|w2&)gwC&mTTjjx^^du`S@suz#b=}H}>!3vVJ7Qh@3 zvH_$6><8c@7Htf=AM77;cXgc}8O`kf`gipMV`K?6r56~>00000NkvXXu0mjfX+XPD literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/32/bigbluebutton_na.png b/plugin/bbb/resources/img/32/bigbluebutton_na.png new file mode 100644 index 0000000000000000000000000000000000000000..332d0736e8be394188b4bcfcc2a5e9f10448e6a4 GIT binary patch literal 1041 zcmV+s1n&EZP) zvv8%j?>}f~B`CPaPFLa&D0Ei}(nahh?MkK~wK0>HZiKWr88NZ71)EHr(Rk14LT%rB zzC2#ErHjtDyXTzyec!$Jo%=4pEBwd6>r6_g(}_?h^qPqDiAYdHnuvG=06&$><@Uc~ zAeYPax7+Q9B9bPeI3fxFh?R$#&j4UYYyEYlQu+QLF_6t>14J|2{&1PeU zVay}q>wbhg7(qlF8yp;b(P%VweH*yg_D4iCfOxI%)M`W!F=d+Ovqq!wi!)ChCQTvY z8#hw6iVz7AeUQuL!p@xG42+D7L=o}6b=#xSXmECRcC^30zt>CX;Nal*wY9aK)6-K1 zI6npvxioRB-EKbwfP$xiSS)szh=SJG!otFXWHR}d_d3rpFfb5VU0wYa5n+CQ{@&8k z(&NhpL#jJyD zHhYVR5>C>K-u4BpwVIoodoVUOc1J`&Yi-+QBD!rI+mg)O=U1o`lF4KO5eeYjxYnAK z`n%bP$fn_R7j+|TwOYS1^9#Qwh)9?jm|ZVTn}*GR*7~JkT=_0xcz8G-kH=$v#7|C6 z&O)J(0RX_v>~e{{w3&4zkK9PRxe2S)YJGiu{hNz$m12fweX)}<|=UGI4aFW()wV(YOs8*{7007I&%cWwmxb4gn z(T;U|rM)I5Cf-&`ePoS=!(lZuGxKI|Z*Ra${P6JbxKJoO?X+Ve@_cG)YQcIDx%T(u z+W3AENvz<#PGhtG27HO{G%7 zNF*{UA}MAbR7yofB*e^r06-HFYf7mfdwP1R8yg#E-EhCc{}+D(!h2TcS|w|L00000 LNkvXXu0mjfHT&oH literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/64/bigbluebutton.png b/plugin/bbb/resources/img/64/bigbluebutton.png new file mode 100644 index 0000000000000000000000000000000000000000..f14e70fe9546139054a3e5f8d87bbf5c2718f138 GIT binary patch literal 1750 zcmV;{1}XW8P);M1&8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H122)8yK~#90?V4+B6-5-s|7T|JrL7R4Af>z#=@ZMVl!p`&kf=z2 zXoMgM0Rt*%KnqyVXf=&dBeV%x3<8NjVv0&38Y7UXkxHPvT99h(g9Z?R7Nvq<3+=sk zj~~Dw(A}MV+?yKj??aPz&TIa=otZtmK$Iv^qC|=Ezl9=^q~F+Ov2oW!*?=4eU=Wc^ zCU8#>bzwk)5g>3gGoNE{JrVz`2l@LGY3IvPQW6ShZDA9j-`HibG1r1mNa3GDph+O= zWIqaD(e=|GE2S?NK!SIX`AjGjTpO!vA68ZtHhUG- zLjm$dTIQwsBz&s@iuW$=op2-TfxOyd?{5OYZlif3K+@0`2Wy(Yn23gY5l;nX{W}gd z&#ODW{+ydQt_4WT%z2uGzX(8_+gSGDi+ZT-sk$GEb~%k8-K0;;T$V?|w+4XUO&sS? z90_f9_uiu~{n2>vu#*Um1xU?YUL=I}3L>CbalR%DUVw+hI&oS&u=>?+4D5`+O(cIhbzSTHvm8R?1e z`CJ__(A?67@}s|C-A8+I>2gcEHc|?NLVEiFi0)4t_UFvn9J$V4QC(e%v<5Tcf-gHQLcbS?d@bc1!&4Wm}#43-a0Lht4#{we5%5Li9 z!SH)9@jFRL>V@=_+aD^W6v)e+g6uI#roKcpe&Ddhsb)5o0!R|GR0>V(-BrcR5q%PG z#nv=|*H=zK_LzYxuH+Mb)A}t1AQF?5GN~9jM2H*FHSOk=AFVX`f1FM0Hx(cwV_7^v zgVnQz2i8LnB8b;sntaEVXXttx%-RA3F*Z5*1;cKXsQ|5h46(9_1VZS@%bkjG4*~DSzt2^DsXMF?Cj5-2y=!EiJ9?n{)=y&7b_Q41s{bA?Qxn`1!?D03wP}?K}2}?}h;b z`l=P=5v{EOB|9R-8jV>jaNPbG^gjkOt{IJ)3&6PQc{L8GsWWT*TK|tS!SFqTsQ@H! z)-rko*t%^$iq?O9_W%)ytdHLWx@o$~{j2XsWJw&#*sedCX;|Z&k9?pSobDC|lH@{Oz{iH*7AoBf?&o?B$jM=!{QP zvV6AVuwm!M4SV)hqxM9@U4P%OxfB~Wms`;lSZ~eEew{0s+UL1?UpuzN(5UX#>Cl9Wng~9xYHYia|9eB( znI4JRr-v%sjvLXu+3uoE*Q( zX=*4tbZ_5rrvOZI6UQS0Ak3?+DBNn-(e>3!ADKH5OvM2DxQ*ouYS2Nm>MB<4bK>o~ z=U-D^SQ^x|;S7A?HkN&OyA(Xa>Er>A|9=N%4^aQMl7>AkP=D zi4*g+hAS;k67fm^UVb^s07}91LCw+CpTmRCWPvcR;AxpFM?zwbV4lu^`Md1)2>{qn zgxw*M+v|?JYuMUyO2oFX!K0S<53v}>OoPCb3UE&dbOVzFpo94mKo?2qzv?VbO6bR~ s_(PRv53Dh4X-A0?B}$YiQKGovZyUhk7}{wom;e9(07*qoM6N<$f^jP+q5uE@ literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/64/bigbluebutton_na.png b/plugin/bbb/resources/img/64/bigbluebutton_na.png new file mode 100644 index 0000000000000000000000000000000000000000..871ba8ce0c0513fb2bf5ea2e09bb345bc83512f4 GIT binary patch literal 1892 zcmV-q2b=hbP);M1&8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12H{CWK~#90?V4R^9M=`c&pG$*tfjayGOE^NtxYW1Mu}B@>@MpV zoTgBWii=Sb*VHL_D)hkum5+rhYIXkQN;Lki4Y0w!wZVrC=j3`(Y=Fs9mqs zG?sRyMz-=LDcYI2rw?6$s%SoTW_M-C{$N4k+e|1_0Ksd~m%HvuZ0 zjsd{S0MH|Z5CFi4r~rT~KR`rg=G8b50Farf@>^l%kD2+-@bK_NJ;bQF0EZ48YFS)d zd{xu5Aw=AYh-RhJzF=m-%)+v)FHF(bKF-wK7oO0D9pr2rU<#SSCl2_o83={VQQ zIWr?OOUtqr0pP=YK7R`;qbLY95um@nzcUmH{T2~-*1F^CStG0RMQh=<~M~9&t~r{2q9XxZQJ&1ujM^=W^i!u z6)9zxzpxqyB627c>K_~&?DtaMlK>}9oDf8Gw2|w45D?KKqSwL0K1X_S_~_B2SEZEC zgUi-4nM`(^JbCg^G#cGOM1l4g<#M?^H8u6w#fukjl}e?hjmk(V$u!LkABqs7V_;w) zaqr%}j8i+FMgS50*lnZJ)6>&-`t<47cJJQZ8MN~N0Gg(0Jv}{p&zw2)b1CI_;?y*a zQmNEW?dl_$ssz~d^@bK_pYisKkzx7*NTRQ+?dLx_L2>5~DMr%os&I14#hG9EF zJB|RQ)E2KRn^MX$3}eORee})RS8=rs%d$M}Oo=FLXLA%l2w^*UtY`c7?Lyae+lK0a zX_}snMrlge1b~d-2;GCQDMj)j$&!0b^zk2oR_4iB0$zl zLzgaHnpj?5ww-MJa{vDQsdPI1op*S2bTo7G=1o@-EX(?*liky4<3vIz;Ap8 zv1wSA^)Q>w{=un?r<`Cqoz5$zW@{5x;~=72u4O#>VrKqxz<3(X+|i>)^KOMaCozk~ z;(vDT+}VkUJA%d47!dJ;%a<<~-O5#U&82DD`%0-V{fE^Ulv1DExpT+$=>t?L!0p?& zzm`(|=~>tv+QH1Xmur$-kG$WIUe{ zRta>KH9b8&6OBd-N~sqSF;L<%0AE^`^>#L!o%B;V@Lk@_%*@lawzjmCvZK*UBs1Sd z#CI~8jPFfaP2b-{BGF4k#}Tov_X*5Aqm=qfHkq{aN-Yx60szbt(PP6fW^%cl(Wn@kG-=YL eNs}huFa8VXP?uO%ms(o@00009&RTqFWhG;MX z0Pr&v0_a?^Vude}NSIY0tPDIUAf>7gevut|HOMsXIca!=!Wdub4>16cM39WD0|Ns} zm4~SaPYOs}y!_3k1OK|M$xPqCK~;+bl}J4|HFN_}&f9Z)+qSE%=gKm8xhauISj(0z zd;5TG?=(%bPF2;aB*Hd38$2g9bc2)<05HBko$#ap0F+E7jqdL5qse4)pjv}3Jo;G# za9tk&@}6)SaoSw17#ri5AL822O<~`4MQknufHB8FZ}y0(Kz)iC)~|n*!thMX@vSIe zON+Kn5S3?K138h06>sLMp{TYhCFPJj@Tqt+%=4kW>S{Zu7Hhk%nvYi&qblO zI>s1u+k!0|5J_;x;ZrmOR1Lbc@u|^2cqlB+>^=x%rsVOI12)e_KtGOPH3kea?ZI}f zz77C}(xVV0CSA|k22`e?FZKH_6oq-iYhfm1hAJHaKW16=mjcxz;EZlA0ERR zP0`j0Ao$OIVrKRioal(?kDTXdpQ?~dObwb9BW>XN z6<^;JL)S~1vL)ECJq@wwT8v=~a2?qT>X*s3Yy)fh30@hF(PRb}-mHqAQXj-`v zfw^i%aIJX`vB&a)UTg?7X90|pHKh*IN1FtKJR|GBtsX10Z_rALV-l747xh8KubAd zo;;vz2mp|hAo6XhggKOga4Ksw*wuyjiWL9=XLJ-_KK^*cq*o3aysdkdQ#s*_@nQf#_Rt~xd*eosQ~g(F#UKV4KAO~zPyt~nWy$hdIY=f^SXCz_K}Ro5E`{*7 zv|!Qt_4(s3o_-qAK0Om6BLK(>1|~q&g$=(K58#veO-v<)JFl;VmTRuTl{aTGsTKd4S~y98aHQ5DW+y5D1fkIx4R**8?UzMTL7v?iI|1AnS-U%qhUKO1K8)DHqSxX;P` zod`N7m`oz%0Di$BB>@Hf0tW!5`979DRz3-whBvmvT+-Qys{k+Li2+1R{5>}702VA5 zeY5@CpL`f=UzgVS@r)mm;4bQb+-0$}N*GgR$=reiz_xJ5Zo!~?OEy&k102=O@R9|W{K*bU3=o{joek~Ja?Wi4V2sh;y?b>uHh%VI z>%6re#@g4Veai7nfXOnPc<~R~G3SyOvYPsFs*c&!cxOz^t=*Q-C7h19E^Y<9mbJ>y zwKXPs>ziLW9EyF=(a}*@iz%5*I(zo)86NkBufz4*Qb8un3VlRg6Fob38hg9u-!c-= z{x%Y0mhDkUVt`O4EUSlH>t-W>5p-SK(empirw#^44ze{S{uzn?VMk-LwQajM8jbcRlSvcazZqc5mMv3KsZ{K;k;6B2=%W{XaM6`NOC%CzC01D_A z82)H98nSIWqN=KoXeLu+c{q+EiAWCygDD||(b3Uim2-P9AR@MM)U!S^f-##6c z=U0=!R;^ma2L}hgwGXPAo13Ypr)Tm+etKY4d{zxL)KEhWHPleU|0ezm*%|#HN$d(F P00000NkvXXu0mjfCv<-o literal 0 HcmV?d00001 diff --git a/plugin/bbb/resources/img/64/videoconference_html5.png b/plugin/bbb/resources/img/64/videoconference_html5.png new file mode 100644 index 0000000000000000000000000000000000000000..4b1dde01f5a7b613b4debd2ef827f968c44aad53 GIT binary patch literal 3371 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 + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/bbb/resources/img/svg/bigbluebutton_na.svg b/plugin/bbb/resources/img/svg/bigbluebutton_na.svg new file mode 100644 index 000000000..305e18356 --- /dev/null +++ b/plugin/bbb/resources/img/svg/bigbluebutton_na.svg @@ -0,0 +1,336 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/bbb/resources/img/svg/videoconference_flash.svg b/plugin/bbb/resources/img/svg/videoconference_flash.svg new file mode 100644 index 000000000..e544a70c4 --- /dev/null +++ b/plugin/bbb/resources/img/svg/videoconference_flash.svg @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/bbb/resources/img/svg/videoconference_html5.svg b/plugin/bbb/resources/img/svg/videoconference_html5.svg new file mode 100644 index 000000000..7b84c93cc --- /dev/null +++ b/plugin/bbb/resources/img/svg/videoconference_html5.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/bbb/resources/utils.js b/plugin/bbb/resources/utils.js new file mode 100644 index 000000000..42ae3c1ab --- /dev/null +++ b/plugin/bbb/resources/utils.js @@ -0,0 +1,27 @@ +$(function () { + $('.check-meeting-video').on('click', function (e) { + e.preventDefault(); + + var $self = $(this), + meetingId = $self.data('id') || 0; + + if (!meetingId) { + return; + } + + var $loader = $('', { + 'aria-hidden': 'true' + }).addClass('fa fa-spinner fa-spin fa-fw'); + + $self.replaceWith($loader); + + $.get(_p.web_plugin + 'bbb/ajax.php', { + a: 'check_m4v', + meeting: meetingId + }, function (response) { + $loader.replaceWith(response.link); + + window.open(response.url); + }); + }); +}); diff --git a/plugin/bbb/start.php b/plugin/bbb/start.php new file mode 100644 index 000000000..8cc970bff --- /dev/null +++ b/plugin/bbb/start.php @@ -0,0 +1,112 @@ + 'Videoconference', +]; +Event::registerLog($logInfo); + +$tool_name = get_lang('Videoconference'); +$tpl = new Template($tool_name); + +$vmIsEnabled = false; +$host = ''; +$salt = ''; +$bbb = new bbb('', '', $isGlobal, $isGlobalPerUser); + +$conferenceManager = $bbb->isConferenceManager(); +if ($bbb->isGlobalConference()) { + api_block_anonymous_users(); +} else { + api_protect_course_script(true); +} + +$message = null; +if ($bbb->pluginEnabled) { + if ($bbb->isServerConfigured()) { + if ($bbb->isServerRunning()) { + if (isset($_GET['launch']) && $_GET['launch'] == 1) { + if (file_exists(__DIR__.'/config.vm.php')) { + $config = require __DIR__.'/config.vm.php'; + $vmIsEnabled = true; + $host = ''; + $salt = ''; + + require __DIR__.'/lib/vm/AbstractVM.php'; + require __DIR__.'/lib/vm/VMInterface.php'; + require __DIR__.'/lib/vm/DigitalOceanVM.php'; + require __DIR__.'/lib/VM.php'; + + $vm = new VM($config); + + if ($vm->isEnabled()) { + try { + $vm->resizeToMaxLimit(); + } catch (\Exception $e) { + echo $e->getMessage(); + exit; + } + } + } + + $meetingParams = []; + $meetingParams['meeting_name'] = $bbb->generateVideoConferenceName($_GET['name'] ?? null); + $url = null; + if ($bbb->meetingExists($meetingParams['meeting_name'])) { + $joinUrl = $bbb->joinMeeting($meetingParams['meeting_name']); + if ($joinUrl) { + $url = $joinUrl; + } + } else { + if ($bbb->isConferenceManager()) { + $url = $bbb->createMeeting($meetingParams); + } + } + + $meetingInfo = $bbb->findMeetingByName($meetingParams['meeting_name']); + if (!empty($meetingInfo) && $url) { + $bbb->saveParticipant($meetingInfo['id'], api_get_user_id()); + $bbb->redirectToBBB($url); + } else { + Display::addFlash( + Display::return_message($bbb->plugin->get_lang('ThereIsNoVideoConferenceActive')) + ); + $url = $bbb->getListingUrl(); + header('Location: '.$url); + exit; + } + } else { + $url = $bbb->getListingUrl(); + header('Location: '.$url); + exit; + } + } else { + $message = Display::return_message(get_lang('ServerIsNotRunning'), 'warning'); + } + } else { + $message = Display::return_message(get_lang('ServerIsNotConfigured'), 'warning'); + } +} else { + $message = Display::return_message(get_lang('ServerIsNotConfigured'), 'warning'); +} + +$tpl->assign('message', $message); +$tpl->display_one_col_template(); diff --git a/plugin/bbb/uninstall.php b/plugin/bbb/uninstall.php new file mode 100644 index 000000000..4bf2dfdfb --- /dev/null +++ b/plugin/bbb/uninstall.php @@ -0,0 +1,12 @@ +uninstall(); diff --git a/plugin/bbb/update.php b/plugin/bbb/update.php new file mode 100644 index 000000000..df3eea673 --- /dev/null +++ b/plugin/bbb/update.php @@ -0,0 +1,14 @@ +update(); diff --git a/plugin/bbb/view/admin.tpl b/plugin/bbb/view/admin.tpl new file mode 100644 index 000000000..6a1f4cb28 --- /dev/null +++ b/plugin/bbb/view/admin.tpl @@ -0,0 +1,58 @@ +{{ settings_form }} + +{{ 'RecordList'|get_plugin_lang('BBBPlugin') }} + +{{ search_form }} + + + + + + + + + + + + + + + + {% for meeting in meetings %} + + {% if meeting.visibility == 0 %} + + {% else %} + + {% endif %} + + + + + + + + + + {% endfor %} + +
{{ 'DateStart'|get_lang }}{{ 'DateEnd'|get_lang }}{{ 'Status'|get_lang }}{{ 'Records'|get_plugin_lang('BBBPlugin') }}{{ 'Course'|get_lang }}{{ 'Session'|get_lang }}{{ 'Participants'|get_lang }}{{ 'CountUsers'|get_lang }}{{ 'Actions'|get_lang }}
{{ meeting.created_at }}{{ meeting.created_at }}{{ meeting.closed_at }} + {% if meeting.status == 1 %} + {{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }} + {% else %} + {{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }} + {% endif %} + + {% if meeting.record == 1 %} + {# Record list #} + {{ meeting.show_links }} + {% else %} + {{ 'NoRecording'|get_plugin_lang('BBBPlugin') }} + {% endif %} + {{ meeting.course ?: '-' }}{{ meeting.session ?: '-' }} + {{ meeting.participants ? meeting.participants|join('
') : '-' }} +
+ {{ meeting.participants ? meeting.participants | length : 0 }} + + {{ meeting.action_links }} +
diff --git a/plugin/bbb/view/listing.tpl b/plugin/bbb/view/listing.tpl new file mode 100644 index 000000000..489288598 --- /dev/null +++ b/plugin/bbb/view/listing.tpl @@ -0,0 +1,146 @@ + +
+{% if bbb_status == true %} +
+ {{ form }} + {% if show_join_button == true %} + {{ enter_conference_links }} +
+ {{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }} +
+
+
+ + +
+
+
+ +

+ + {{ 'XUsersOnLine'| get_plugin_lang('BBBPlugin') | format(users_online) }} + +

+ + {% if max_users_limit > 0 %} + {% if conference_manager == true %} +

{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}

+ {% elseif users_online >= max_users_limit/2 %} +

{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}

+ {% endif %} + {% endif %} +
+
+ {% elseif max_users_limit > 0 %} + {% if conference_manager == true %} +

{{ 'MaxXUsersReachedManager' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}

+ {% elseif users_online > 0 %} +

{{ 'MaxXUsersReached' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}

+ {% endif %} + {% endif %} +
+ +
+ + + + + + + + {% if allow_to_edit %} + + {% endif %} + + {% for meeting in meetings %} + + + + {% if meeting.visibility == 0 %} + + {% else %} + + {% endif %} + + + {% if allow_to_edit %} + + {% endif %} + + + {% endfor %} +
{{ 'Name'|get_lang }}{{ 'CreatedAt'| get_plugin_lang('BBBPlugin') }}{{ 'Status'| get_lang }}{{ 'Records'| get_plugin_lang('BBBPlugin') }}{{ 'Actions'| get_lang }}
{{ meeting.metting_name }}{{ meeting.created_at }}{{ meeting.created_at }} + {% if meeting.status == 1 %} + {{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }} + {% else %} + {{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }} + {% endif %} + + {% if meeting.show_links.record %} + {# Record list #} + {% for link in meeting.show_links %} + {% if link is not iterable %} + {{ link }} + {% endif %} + {% endfor %} + {% else %} + {{ 'NoRecording'|get_plugin_lang('BBBPlugin') }} + {% endif %} + + + {% if meeting.status == 1 %} + + {{ 'CloseMeeting'|get_plugin_lang('BBBPlugin') }} + + {% endif %} + {{ meeting.action_links }} +
+
+ +{% else %} +
+ {{ 'ServerIsNotRunning' | get_plugin_lang('BBBPlugin') | return_message('warning') }} +
+{% endif %} + diff --git a/plugin/before_login/README.md b/plugin/before_login/README.md new file mode 100644 index 000000000..6c3609867 --- /dev/null +++ b/plugin/before_login/README.md @@ -0,0 +1,14 @@ +Before login plugin +=== + +This plugins shows a page before showing the login page. It is a bit rough and requires theming work to make anything appealing. + +This is useful if you want to ask a question before a user has the opportunity to login. + +Example: +You're old enough to enter this site? If you accept the "option 1" then you can enter to the campus (the login will appear). + +If you select "option 2" then you're redirected to another page. + +To enable, install the plugin then add it to the content_top or main_top region. +Theme it by creating custom.template.tpl from template.tpl in the plugin/before_login/ directory. \ No newline at end of file diff --git a/plugin/before_login/index.php b/plugin/before_login/index.php new file mode 100644 index 000000000..d171a7547 --- /dev/null +++ b/plugin/before_login/index.php @@ -0,0 +1,76 @@ +defaultRenderer(); + $renderer->setFormTemplate('{content}
'); + $renderer->setCustomElementTemplate('{element}'); + + $form->addElement('html', $option1); + $form->addElement('checkbox', 'left', null, get_lang('Yes')); + $form->addElement('button', 'submit', get_lang('Confirm'), ['class' => 'btn btn-primary']); + $formHtml = $form->returnForm(); + if ($form->validate()) { + $result = $form->getSubmitValues(); + if (isset($result['left']) && $result['left']) { + $_SESSION['before_login_accepted'] = 1; + header('Location: '.$urlOption1); + exit; + } + } + + $form2 = new FormValidator('form'); + + if (!empty($option2) && !empty($urlOption2)) { + $renderer = &$form2->defaultRenderer(); + $renderer->setHeaderTemplate(''); + $renderer->setFormTemplate('{content}
'); + $renderer->setCustomElementTemplate('{element}'); + + $form2->addElement('html', $option2); + $form2->addElement('checkbox', 'right', null, get_lang('Yes')); + $form2->addElement('button', 'submit', get_lang('Send')); + $formHtml2 = $form2->returnForm(); + + if ($form2->validate()) { + $result = $form2->getSubmitValues(); + if (isset($result['right']) && $result['right']) { + header('Location: '.$urlOption2); + exit; + } + } + } + + $_template['form_option1'] = $formHtml; + $_template['form_option2'] = $formHtml2; +} diff --git a/plugin/before_login/plugin.php b/plugin/before_login/plugin.php new file mode 100644 index 000000000..f93f91898 --- /dev/null +++ b/plugin/before_login/plugin.php @@ -0,0 +1,53 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ + +/* Plugin config */ + +// The plugin title. +$plugin_info['title'] = 'Show HTML before login'; +// The comments that go with the plugin. +$plugin_info['comment'] = "Show a content before loading the login page."; +// The plugin version. +$plugin_info['version'] = '1.0'; +// The plugin author. +$plugin_info['author'] = 'Julio Montoya'; + +// The plugin configuration. +$form = new FormValidator('form'); +$form->addElement('select', 'language', get_lang('Language'), api_get_languages_to_array()); + +$form->addElement('header', 'Option 1'); +$form->addElement('textarea', 'option1', get_lang('Description'), ['rows' => 10, 'class' => 'span6']); +$form->addElement('text', 'option1_url', get_lang('RedirectTo')); + +$form->addElement('header', 'Option 2'); +$form->addElement('textarea', 'option2', get_lang('Description'), ['rows' => 10, 'class' => 'span6']); +$form->addElement('text', 'option2_url', get_lang('RedirectTo')); +$form->addElement('button', 'submit_button', get_lang('Save')); + +// Get default value for form + +$defaults = []; +$defaults['language'] = api_get_plugin_setting('before_login', 'language'); +$defaults['option1'] = api_get_plugin_setting('before_login', 'option1'); +$defaults['option2'] = api_get_plugin_setting('before_login', 'option2'); + +$defaults['option1_url'] = api_get_plugin_setting('before_login', 'option1_url'); +$defaults['option2_url'] = api_get_plugin_setting('before_login', 'option2_url'); + +$plugin_info['templates'] = ['template.tpl']; +if (file_exists(__DIR__.'/custom.template.tpl')) { + $plugin_info['templates'] = ['custom.template.tpl']; +} +$form->setDefaults($defaults); + +// Display form +$plugin_info['settings_form'] = $form; diff --git a/plugin/before_login/template.tpl b/plugin/before_login/template.tpl new file mode 100644 index 000000000..77fa85e4b --- /dev/null +++ b/plugin/before_login/template.tpl @@ -0,0 +1,14 @@ +{% if before_login.form_option1 %} +
+
+
+ {{ before_login.form_option1 }} +
+
+ {{ before_login.form_option2 }} +
+
+
+{% else %} + +{% endif %} \ No newline at end of file diff --git a/plugin/buycourses/CHANGELOG.md b/plugin/buycourses/CHANGELOG.md new file mode 100644 index 000000000..6deee5944 --- /dev/null +++ b/plugin/buycourses/CHANGELOG.md @@ -0,0 +1,134 @@ +v7.4 - 2022-04-28 +==== +Add subscriptions support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/buycourses/update.php in your browser) to update the database structure by adding the plugin_buycourses_subscription, plugin_buycourses_subscription_rel_sale, plugin_buycourses_subscription_period and plugin_buycourses_coupon_rel_subscription_sale. + +v7.3 - 2022-04-28 +==== +Add Cecabank payments support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/buycourses/update.php in your browser) to update the structure of the tables in the database. + +v7.2 - 2021-11-22 +==== +Add Stripe payments support. + +If the plugin has already been installed, the update.php script must be executed (load plugin/buycourses/update.php in your browser) to update the structure of the tables in the database. + +v7.1 - 2021-10-26 +==== +Fix install issue with DB field type. + +v7.0 - 2021-08-12 +==== +Added support for discount coupons. +Added a better table view for the sales report. +Multiple fixes to navigation-related issues. + +WARNING: Updating this plugin (or Chamilo) without going through the specific update procedure for this plugin will break your sales pages. + +The file [your-host]/plugin/buycourses/update.php *MUST* be executed to update the structure of the tables +in the database. + +v6.0 - 2020-11-29 +==== +Added support for purchase instructions e-mail customization (although this +does not support multiple languages at the moment). +This requires changes to the DB tables: +```sql +ALTER TABLE plugin_buycourses_global_config ADD COLUMN info_email_extra TEXT; +``` + +v5.0 - 2019-02-06 +==== + +This version includes two additional modules (taxes and invoices), +which can be enabled from the configuration. + +The file update.php must be executed to update the structure of the tables + in the database. + + +v4.0 - 2017-04-25 +==== + +This version includes the Culqi payment gateway v1.0 (now expired) and introduces +an additional option to show the Buy Courses tab to anonymous users. + +To enable these features, if you have already installed this plugin on your +portal prior to this version, you will need to add the corresponding settings +to your settings_current table. No documentation is available at this time on +how to do that, so please check up the code. Sorry about that. + + +v3.0 - 2015-09-25 +==== + +This version has been fixed and improved for Chamilo LMS 1.10.x. + +- A new user interface for platform admins and users. +- Avoid data redundancy by adding courses/sessions to catalog +- The catalog of sessions can be configured to offer some courses or sessions +in a currency other than the others courses or sessions +- The sales have a status: Pending, Completed, Canceled +- The Peding Orders pages has been replaced by a Sales Report. +Allowing filter the sales by its status +- The plugin Registration page was removed. Instead the Chamilo LMS +registrarion page is used. +- Added the ability to record beneficiaries with the sale of courses/sessions + +##Changes in database structure + +The database structure has been changed totally. The previous database +structure was formed for the tables: + +- `plugin_buy_course` The registered courses in the platform +- `plugin_buy_course_country` The list of countries with their currencies +- `plugin_buy_course_paypal` The PayPal account info +- `plugin_buy_course_sale` The sales of courses that were made +- `plugin_buy_course_temporal` The pending orders of courses that were made +- `plugin_buy_course_transfer` The bank accounts for transfers +- `plugin_buy_session` The registered courses in the platform +- `plugin_buy_session_course` The courses in sessions +- `plugin_buy_session_sale` The sales of session that were made +- `plugin_buy_session_temporary` The pending orders of session that were made + +To avoid the data redundancy, the `plugin_buy_course`, `plugin_buy_session` +and `plugin_buy_session_course` tables were replaced for the +`plugin_buycourses_item` table. And the `plugin_buy_course_sale`, +`plugin_buy_course_temporal`, `plugin_buy_session_sale` and +`plugin_buy_session_temporary` tables were replaced for the + `plugin_buycourses_item` table. + +The __new database__ structure is formed for the tables: + +- `plugin_buycourses_currency` The list of countries with their currencies +- `plugin_buycourses_item` The registered courses and sessions in the platform +- `plugin_buycourses_item_re_beneficiary` The beneficiaries users with the sale of courses +- `plugin_buycourses_paypal_account` The PayPal account info +- `plugin_buycourses_sale` The sales of courses and sessions that were made +- `plugin_buycourses_transfer` The bank accounts for transfers + +--- + +v2.0 - 2014-10-14 +================= +This version adds support for sales of sessions access. +A session can be purchased as soon as it is given a price, granted the current +date is either previous to the session start date, between the start and end, +or no date has been defined for the session. +Students are subscribed automatically once they have paid. There is no +intermediary step. +This version does not work (yet) with the session period defined by user +(a special feature introduced in Chamilo 1.9.10). + +Upgrade procedure +----------------- +If you are working with this plugin since earlier versions, you will have to +look at the installer to *fix* your plugin tables (add a few fields). + +v1.0 - 2014-06-30 (or something) +================= +This is the first release of the plugin, with only the PayPal payment method +in working state and only for courses. diff --git a/plugin/buycourses/README.md b/plugin/buycourses/README.md new file mode 100644 index 000000000..0aca81db4 --- /dev/null +++ b/plugin/buycourses/README.md @@ -0,0 +1,37 @@ +Buy Courses (course sales) plugin +================================= +This plugin transforms your Chamilo installation in an online shop by adding a catalogue + of courses and sessions that you have previously configured for sales. + +If the user is not registered or logged in, he/she will be requested to register/login +before he/she can resume buying items. + +Do not enable this plugin in any "Region". This is a known issue, but it works without +region assignation. + +Once the course or session is chosen, the plugin displays the available payment methods +and lets the user proceed with the purchase. +Currently, the plugin allows users to pay through: + - PayPal (requires a merchant account on PayPal at configuration time) + - Bank payments (requires manual confirmation of payments' reception) + - RedSys payments (Spanish payment gateway) (requires the download of an external file) + - Stripe payments (requieres a merchant account oin Stripe at configuration time) + - Cecabank payments (Spanish payment gateway) + +The user receives an e-mail confirming the purchase and she/he can immediately +access to the course or session. + +We recommend using sessions as this gives you more time-related availability options +(in the session configuration). + +Updates +========= + +You must load the update.php script for installations that were in +production before updating the code, as it will update the database structure to +enable new features. + +Please note that updating Chamilo does *NOT* automatically update the plugins +structure. + +You can find a history of changes in the [CHANGELOG.md file](../../plugin/buycourses/CHANGELOG.md) diff --git a/plugin/buycourses/admin.php b/plugin/buycourses/admin.php new file mode 100644 index 000000000..f1a1f640f --- /dev/null +++ b/plugin/buycourses/admin.php @@ -0,0 +1,3 @@ +getConnection(); +$platform = $connection->getDatabasePlatform(); +$sm = $connection->getSchemaManager(); + +// Create tables +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_PAYPAL)) { + $paypalTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_PAYPAL); + $paypalTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $paypalTable->addColumn('username', Types::STRING); + $paypalTable->addColumn('password', Types::STRING); + $paypalTable->addColumn('signature', Types::STRING); + $paypalTable->addColumn('sandbox', Types::BOOLEAN); + $paypalTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_TRANSFER)) { + $transferTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TRANSFER); + $transferTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $transferTable->addColumn('name', Types::STRING); + $transferTable->addColumn('account', Types::STRING); + $transferTable->addColumn('swift', Types::STRING); + $transferTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_TPV_REDSYS)) { + $tpvRedsysTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TPV_REDSYS); + $tpvRedsysTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $tpvRedsysTable->addColumn('merchantcode', Types::STRING); + $tpvRedsysTable->addColumn('terminal', Types::STRING); + $tpvRedsysTable->addColumn('currency', Types::STRING); + $tpvRedsysTable->addColumn('kc', Types::STRING); + $tpvRedsysTable->addColumn('url_redsys', Types::STRING); + $tpvRedsysTable->addColumn('url_redsys_sandbox', Types::STRING); + $tpvRedsysTable->addColumn('sandbox', Types::BOOLEAN); + $tpvRedsysTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_CURRENCY)) { + $currencyTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_CURRENCY); + $currencyTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $currencyTable->addColumn( + 'country_code', + Types::STRING, + ['length' => 2] + ); + $currencyTable->addColumn( + 'country_name', + Types::STRING, + ['length' => 255] + ); + $currencyTable->addColumn( + 'iso_code', + Types::STRING, + ['length' => 3] + ); + $currencyTable->addColumn('status', Types::BOOLEAN); + $currencyTable->addUniqueIndex(['country_code']); + $currencyTable->addIndex(['iso_code']); + $currencyTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_ITEM)) { + $itemTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_ITEM); + $itemTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $itemTable->addColumn('product_type', Types::INTEGER); + $itemTable->addColumn( + 'product_id', + Types::INTEGER, + ['unsigned' => true] + ); + $itemTable->addColumn( + 'price', + Types::DECIMAL, + ['scale' => 2] + ); + $itemTable->addColumn( + 'currency_id', + Types::INTEGER, + ['unsigned' => true] + ); + $itemTable->addColumn( + 'tax_perc', + Types::INTEGER, + ['unsigned' => true, 'notnull' => false] + ); + $itemTable->setPrimaryKey(['id']); + $itemTable->addForeignKeyConstraint( + $currencyTable, + ['currency_id'], + ['id'], + ['onDelete' => 'CASCADE'] + ); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY)) { + $itemBeneficiary = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY); + $itemBeneficiary->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $itemBeneficiary->addColumn( + 'item_id', + Types::INTEGER, + ['unsigned' => true] + ); + $itemBeneficiary->addColumn( + 'user_id', + Types::INTEGER, + ['unsigned' => true] + ); + $itemBeneficiary->addColumn( + 'commissions', + Types::INTEGER, + ['unsigned' => true] + ); + $itemBeneficiary->setPrimaryKey(['id']); + $itemBeneficiary->addForeignKeyConstraint( + $itemTable, + ['item_id'], + ['id'], + ['onDelete' => 'CASCADE'] + ); +} +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COMMISSION)) { + $commissions = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COMMISSION); + $commissions->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $commissions->addColumn( + 'commission', + Types::INTEGER, + ['unsigned' => true] + ); + $commissions->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS)) { + $saleCommissions = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS); + $saleCommissions->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $saleCommissions->addColumn('date', Types::DATETIME_MUTABLE); + $saleCommissions->addColumn('payout_date', Types::DATETIME_MUTABLE); + $saleCommissions->addColumn( + 'sale_id', + Types::INTEGER, + ['unsigned' => true] + ); + $saleCommissions->addColumn( + 'user_id', + Types::INTEGER, + ['unsigned' => true] + ); + $saleCommissions->addColumn( + 'commission', + Types::DECIMAL, + ['scale' => 2] + ); + $saleCommissions->addColumn( + 'status', + Types::INTEGER, + ['unsigned' => true] + ); + $saleCommissions->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SALE)) { + $saleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SALE); + $saleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $saleTable->addColumn('reference', Types::STRING); + $saleTable->addColumn('date', Types::DATETIME_MUTABLE); + $saleTable->addColumn( + 'user_id', + Types::INTEGER, + ['unsigned' => true] + ); + $saleTable->addColumn('product_type', Types::INTEGER); + $saleTable->addColumn('product_name', Types::STRING); + $saleTable->addColumn( + 'product_id', + Types::INTEGER, + ['unsigned' => true] + ); + $saleTable->addColumn( + 'price', + Types::DECIMAL, + ['scale' => 2] + ); + $saleTable->addColumn( + 'price_without_tax', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $saleTable->addColumn( + 'tax_perc', + Types::INTEGER, + ['unsigned' => true, 'notnull' => false] + ); + $saleTable->addColumn( + 'tax_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $saleTable->addColumn( + 'currency_id', + Types::INTEGER, + ['unsigned' => true] + ); + $saleTable->addColumn('status', Types::INTEGER); + $saleTable->addColumn('payment_type', Types::INTEGER); + $saleTable->addColumn('invoice', Types::INTEGER); + $saleTable->addColumn( + 'price_without_discount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $saleTable->addColumn( + 'discount_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $saleTable->setPrimaryKey(['id']); + $saleTable->addForeignKeyConstraint( + $currencyTable, + ['currency_id'], + ['id'], + ['onDelete' => 'CASCADE'] + ); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SERVICES)) { + $servicesTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SERVICES); + $servicesTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $servicesTable->addColumn('name', Types::STRING); + $servicesTable->addColumn('description', Types::TEXT); + $servicesTable->addColumn( + 'price', + Types::DECIMAL, + ['scale' => 2] + ); + $servicesTable->addColumn('duration_days', Types::INTEGER); + $servicesTable->addColumn('applies_to', Types::INTEGER); + $servicesTable->addColumn('owner_id', Types::INTEGER); + $servicesTable->addColumn('visibility', Types::INTEGER); + $servicesTable->addColumn('video_url', Types::STRING); + $servicesTable->addColumn('image', Types::STRING); + $servicesTable->addColumn('service_information', Types::TEXT); + $servicesTable->addColumn('tax_perc', Types::INTEGER); + $servicesTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SERVICES_SALE)) { + $servicesNodeTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SERVICES_SALE); + $servicesNodeTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $servicesNodeTable->addColumn( + 'service_id', + Types::INTEGER, + ['unsigned' => true] + ); + $servicesNodeTable->addColumn('reference', Types::STRING); + $servicesNodeTable->addColumn('currency_id', Types::INTEGER); + $servicesNodeTable->addColumn( + 'price', + Types::DECIMAL, + ['scale' => 2] + ); + $servicesNodeTable->addColumn( + 'price_without_tax', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $servicesNodeTable->addColumn( + 'tax_perc', + Types::INTEGER, + ['unsigned' => true, 'notnull' => false] + ); + $servicesNodeTable->addColumn( + 'tax_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $servicesNodeTable->addColumn('node_type', Types::INTEGER); + $servicesNodeTable->addColumn('node_id', Types::INTEGER); + $servicesNodeTable->addColumn('buyer_id', Types::INTEGER); + $servicesNodeTable->addColumn('buy_date', Types::DATETIME_MUTABLE); + $servicesNodeTable->addColumn( + 'date_start', + Types::DATETIME_MUTABLE, + ['notnull' => false] + ); + $servicesNodeTable->addColumn( + 'date_end', + Types::DATETIME_MUTABLE + ); + $servicesNodeTable->addColumn('status', Types::INTEGER); + $servicesNodeTable->addColumn('payment_type', Types::INTEGER); + $servicesNodeTable->addColumn('invoice', Types::INTEGER); + $servicesNodeTable->addColumn( + 'price_without_discount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $servicesNodeTable->addColumn( + 'discount_amount', + Types::DECIMAL, + ['scale' => 2, 'notnull' => false] + ); + $servicesNodeTable->setPrimaryKey(['id']); + $servicesNodeTable->addForeignKeyConstraint( + $servicesTable, + ['service_id'], + ['id'], + ['onDelete' => 'CASCADE'] + ); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_CULQI)) { + $culqiTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_CULQI); + $culqiTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $culqiTable->addColumn('commerce_code', Types::STRING); + $culqiTable->addColumn('api_key', Types::STRING); + $culqiTable->addColumn('integration', Types::INTEGER); + $culqiTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG)) { + $globalTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); + $globalTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $globalTable->addColumn('terms_and_conditions', Types::TEXT); + $globalTable->addColumn('global_tax_perc', Types::INTEGER); + $globalTable->addColumn('tax_applies_to', Types::INTEGER); + $globalTable->addColumn('tax_name', Types::STRING); + $globalTable->addColumn('seller_name', Types::STRING); + $globalTable->addColumn('seller_id', Types::STRING); + $globalTable->addColumn('seller_address', Types::STRING); + $globalTable->addColumn('seller_email', Types::STRING); + $globalTable->addColumn('next_number_invoice', Types::INTEGER); + $globalTable->addColumn('invoice_series', Types::STRING); + $globalTable->addColumn('sale_email', Types::STRING); + $globalTable->addColumn('info_email_extra', Types::TEXT); + $globalTable->setPrimaryKey(['id']); +} else { + $globalTable = $pluginSchema->getTable(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); + + if (!$globalTable->hasColumn('info_email_extra')) { + $globalTable->addColumn('info_email_extra', Types::TEXT); + } +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_INVOICE)) { + $invoiceTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_INVOICE); + $invoiceTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $invoiceTable->addColumn('sale_id', Types::INTEGER); + $invoiceTable->addColumn('is_service', Types::INTEGER); + $invoiceTable->addColumn( + 'num_invoice', + Types::INTEGER, + ['unsigned' => true, 'notnull' => false] + ); + $invoiceTable->addColumn( + 'year', + Types::INTEGER, + ['unsigned' => true, 'notnull' => false] + ); + $invoiceTable->addColumn('serie', Types::STRING); + $invoiceTable->addColumn('date_invoice', Types::DATETIME_MUTABLE); + $invoiceTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON)) { + $couponTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON); + $couponTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponTable->addColumn('code', Types::STRING); + $couponTable->addColumn('discount_type', Types::INTEGER); + $couponTable->addColumn('discount_amount', Types::INTEGER); + $couponTable->addColumn('valid_start', Types::DATETIME_MUTABLE); + $couponTable->addColumn('valid_end', Types::DATETIME_MUTABLE); + $couponTable->addColumn('delivered', Types::INTEGER); + $couponTable->addColumn('active', Types::BOOLEAN); + $couponTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_ITEM)) { + $couponItemTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_ITEM); + $couponItemTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponItemTable->addColumn('coupon_id', Types::INTEGER); + $couponItemTable->addColumn('product_type', Types::INTEGER); + $couponItemTable->addColumn('product_id', Types::INTEGER); + $couponItemTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SERVICE)) { + $couponService = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SERVICE); + $couponService->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponService->addColumn('coupon_id', Types::INTEGER); + $couponService->addColumn('service_id', Types::INTEGER); + $couponService->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION)) { + $subscriptionTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION); + $subscriptionTable->addColumn( + 'product_type', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn( + 'product_id', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn( + 'duration', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionTable->addColumn('currency_id', Types::INTEGER); + $subscriptionTable->addColumn('price', Types::DECIMAL); + $subscriptionTable->addColumn('tax_perc', Types::INTEGER); + $subscriptionTable->setPrimaryKey(['product_type', 'product_id', 'duration']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION_SALE)) { + $subscriptionSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION_SALE); + $subscriptionSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $subscriptionSaleTable->addColumn('currency_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('reference', Types::STRING); + $subscriptionSaleTable->addColumn('date', Types::DATETIME_MUTABLE); + $subscriptionSaleTable->addColumn('user_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('product_type', Types::INTEGER); + $subscriptionSaleTable->addColumn('product_name', Types::STRING); + $subscriptionSaleTable->addColumn('product_id', Types::INTEGER); + $subscriptionSaleTable->addColumn('price', Types::DECIMAL); + $subscriptionSaleTable->addColumn('price_without_tax', Types::DECIMAL, ['notnull' => false]); + $subscriptionSaleTable->addColumn('tax_perc', Types::INTEGER, ['notnull' => false]); + $subscriptionSaleTable->addColumn('tax_amount', Types::DECIMAL, ['notnull' => false]); + $subscriptionSaleTable->addColumn('status', Types::INTEGER); + $subscriptionSaleTable->addColumn('payment_type', Types::INTEGER); + $subscriptionSaleTable->addColumn('invoice', Types::INTEGER); + $subscriptionSaleTable->addColumn('price_without_discount', Types::DECIMAL); + $subscriptionSaleTable->addColumn('discount_amount', Types::DECIMAL); + $subscriptionSaleTable->addColumn('subscription_end', Types::DATETIME_MUTABLE); + $subscriptionSaleTable->addColumn('expired', Types::BOOLEAN); + $subscriptionSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_SUBSCRIPTION_PERIOD)) { + $subscriptionPeriodTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_SUBSCRIPTION_PERIOD); + $subscriptionPeriodTable->addColumn( + 'duration', + Types::INTEGER, + ['unsigned' => true] + ); + $subscriptionPeriodTable->addColumn('name', Types::STRING); + $subscriptionPeriodTable->setPrimaryKey(['duration']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SALE)) { + $couponSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SALE); + $couponSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSaleTable->addColumn('sale_id', Types::INTEGER); + $couponSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SERVICE_SALE)) { + $couponSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SERVICE_SALE); + $couponSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSaleTable->addColumn('service_sale_id', Types::INTEGER); + $couponSaleTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_STRIPE)) { + $stripeTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_STRIPE); + $stripeTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $stripeTable->addColumn('account_id', Types::STRING); + $stripeTable->addColumn('secret_key', Types::STRING); + $stripeTable->addColumn('endpoint_secret', Types::STRING); + $stripeTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_TPV_CECABANK)) { + $tpvCecabankTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_TPV_CECABANK); + $tpvCecabankTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $tpvCecabankTable->addColumn('crypto_key', Types::STRING); + $tpvCecabankTable->addColumn('merchant_id', Types::STRING); + $tpvCecabankTable->addColumn('acquirer_bin', Types::STRING); + $tpvCecabankTable->addColumn('terminal_id', Types::STRING); + $tpvCecabankTable->addColumn('cypher', Types::STRING); + $tpvCecabankTable->addColumn('exponent', Types::STRING); + $tpvCecabankTable->addColumn('supported_payment', Types::STRING); + $tpvCecabankTable->addColumn('url', Types::STRING); + $tpvCecabankTable->setPrimaryKey(['id']); +} + +if (false === $sm->tablesExist(BuyCoursesPlugin::TABLE_COUPON_SUBSCRIPTION_SALE)) { + $couponSubscriptionSaleTable = $pluginSchema->createTable(BuyCoursesPlugin::TABLE_COUPON_SUBSCRIPTION_SALE); + $couponSubscriptionSaleTable->addColumn( + 'id', + Types::INTEGER, + ['autoincrement' => true, 'unsigned' => true] + ); + $couponSubscriptionSaleTable->addColumn('coupon_id', Types::INTEGER); + $couponSubscriptionSaleTable->addColumn('sale_id', Types::INTEGER); + $couponSubscriptionSaleTable->setPrimaryKey(['id']); +} + +$queries = $pluginSchema->toSql($platform); + +foreach ($queries as $query) { + Database::query($query); +} + +// Insert data +$paypalTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL); +$currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY); +$itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM); +$saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE); +$commissionTable = Database::get_main_table(BuyCoursesPlugin::TABLE_COMMISSION); +$extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD); +$culqiTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CULQI); +$globalTable = Database::get_main_table(BuyCoursesPlugin::TABLE_GLOBAL_CONFIG); +$tpvRedsysTable = Database::get_main_table(BuyCoursesPlugin::TABLE_TPV_REDSYS); +$stripeTable = Database::get_main_table(BuyCoursesPlugin::TABLE_STRIPE); + +$paypalExtraField = Database::select( + "*", + $extraFieldTable, + [ + 'where' => ['variable = ?' => 'paypal'], + ], + 'first' +); + +if (!$paypalExtraField) { + Database::insert( + $extraFieldTable, + [ + 'extra_field_type' => 1, + 'field_type' => 1, + 'variable' => 'paypal', + 'display_text' => 'Paypal', + 'default_value' => '', + 'field_order' => 0, + 'visible_to_self' => 1, + 'changeable' => 1, + 'filter' => 0, + 'created_at' => api_get_utc_datetime(), + ] + ); +} + +Database::insert( + $paypalTable, + [ + 'username' => '', + 'password' => '', + 'signature' => '', + 'sandbox' => true, + ] +); + +Database::insert( + $tpvRedsysTable, + [ + 'url_redsys' => 'https://sis.redsys.es/sis/realizarPago', + 'url_redsys_sandbox' => 'https://sis-t.redsys.es:25443/sis/realizarPago', + ] +); + +Database::insert( + $culqiTable, + [ + 'commerce_code' => '', + 'api_key' => '', + 'integration' => 1, + ] +); + +Database::insert( + $globalTable, + [ + 'terms_and_conditions' => '', + ] +); + +Database::insert( + $commissionTable, + [ + 'commission' => 0, + ] +); + +Database::insert( + $stripeTable, + [ + 'account_id' => '', + 'secret_key' => '', + 'endpoint_secret' => '', + ] +); + +$currencies = [ + ['AD', 'Andorra', 'EUR', 'AND'], + ['AE', 'United Arab Emirates', 'AED', 'ARE'], + ['AF', 'Afghanistan', 'AFN', 'AFG'], + ['AG', 'Antigua and Barbuda', 'XCD', 'ATG'], + ['AI', 'Anguilla', 'XCD', 'AIA'], + ['AL', 'Albania', 'ALL', 'ALB'], + ['AM', 'Armenia', 'AMD', 'ARM'], + ['AO', 'Angola', 'AOA', 'AGO'], + ['AR', 'Argentina', 'ARS', 'ARG'], + ['AS', 'American Samoa', 'USD', 'ASM'], + ['AT', 'Austria', 'EUR', 'AUT'], + ['AU', 'Australia', 'AUD', 'AUS'], + ['AW', 'Aruba', 'AWG', 'ABW'], + ['AX', 'Åland', 'EUR', 'ALA'], + ['AZ', 'Azerbaijan', 'AZN', 'AZE'], + ['BA', 'Bosnia and Herzegovina', 'BAM', 'BIH'], + ['BB', 'Barbados', 'BBD', 'BRB'], + ['BD', 'Bangladesh', 'BDT', 'BGD'], + ['BE', 'Belgium', 'EUR', 'BEL'], + ['BF', 'Burkina Faso', 'XOF', 'BFA'], + ['BG', 'Bulgaria', 'BGN', 'BGR'], + ['BH', 'Bahrain', 'BHD', 'BHR'], + ['BI', 'Burundi', 'BIF', 'BDI'], + ['BJ', 'Benin', 'XOF', 'BEN'], + ['BL', 'Saint Barthélemy', 'EUR', 'BLM'], + ['BM', 'Bermuda', 'BMD', 'BMU'], + ['BN', 'Brunei', 'BND', 'BRN'], + ['BO', 'Bolivia', 'BOB', 'BOL'], + ['BQ', 'Bonaire', 'USD', 'BES'], + ['BR', 'Brazil', 'BRL', 'BRA'], + ['BS', 'Bahamas', 'BSD', 'BHS'], + ['BT', 'Bhutan', 'BTN', 'BTN'], + ['BV', 'Bouvet Island', 'NOK', 'BVT'], + ['BW', 'Botswana', 'BWP', 'BWA'], + ['BY', 'Belarus', 'BYR', 'BLR'], + ['BZ', 'Belize', 'BZD', 'BLZ'], + ['CA', 'Canada', 'CAD', 'CAN'], + ['CC', 'Cocos [Keeling] Islands', 'AUD', 'CCK'], + ['CD', 'Congo', 'CDF', 'COD'], + ['CF', 'Central African Republic', 'XAF', 'CAF'], + ['CG', 'Republic of the Congo', 'XAF', 'COG'], + ['CH', 'Switzerland', 'CHF', 'CHE'], + ['CI', 'Ivory Coast', 'XOF', 'CIV'], + ['CK', 'Cook Islands', 'NZD', 'COK'], + ['CL', 'Chile', 'CLP', 'CHL'], + ['CM', 'Cameroon', 'XAF', 'CMR'], + ['CN', 'China', 'CNY', 'CHN'], + ['CO', 'Colombia', 'COP', 'COL'], + ['CR', 'Costa Rica', 'CRC', 'CRI'], + ['CU', 'Cuba', 'CUP', 'CUB'], + ['CV', 'Cape Verde', 'CVE', 'CPV'], + ['CW', 'Curacao', 'ANG', 'CUW'], + ['CX', 'Christmas Island', 'AUD', 'CXR'], + ['CY', 'Cyprus', 'EUR', 'CYP'], + ['CZ', 'Czechia', 'CZK', 'CZE'], + ['DE', 'Germany', 'EUR', 'DEU'], + ['DJ', 'Djibouti', 'DJF', 'DJI'], + ['DK', 'Denmark', 'DKK', 'DNK'], + ['DM', 'Dominica', 'XCD', 'DMA'], + ['DO', 'Dominican Republic', 'DOP', 'DOM'], + ['DZ', 'Algeria', 'DZD', 'DZA'], + ['EC', 'Ecuador', 'USD', 'ECU'], + ['EE', 'Estonia', 'EUR', 'EST'], + ['EG', 'Egypt', 'EGP', 'EGY'], + ['EH', 'Western Sahara', 'MAD', 'ESH'], + ['ER', 'Eritrea', 'ERN', 'ERI'], + ['ES', 'Spain', 'EUR', 'ESP'], + ['ET', 'Ethiopia', 'ETB', 'ETH'], + ['FI', 'Finland', 'EUR', 'FIN'], + ['FJ', 'Fiji', 'FJD', 'FJI'], + ['FK', 'Falkland Islands', 'FKP', 'FLK'], + ['FM', 'Micronesia', 'USD', 'FSM'], + ['FO', 'Faroe Islands', 'DKK', 'FRO'], + ['FR', 'France', 'EUR', 'FRA'], + ['GA', 'Gabon', 'XAF', 'GAB'], + ['GB', 'United Kingdom', 'GBP', 'GBR'], + ['GD', 'Grenada', 'XCD', 'GRD'], + ['GE', 'Georgia', 'GEL', 'GEO'], + ['GF', 'French Guiana', 'EUR', 'GUF'], + ['GG', 'Guernsey', 'GBP', 'GGY'], + ['GH', 'Ghana', 'GHS', 'GHA'], + ['GI', 'Gibraltar', 'GIP', 'GIB'], + ['GL', 'Greenland', 'DKK', 'GRL'], + ['GM', 'Gambia', 'GMD', 'GMB'], + ['GN', 'Guinea', 'GNF', 'GIN'], + ['GP', 'Guadeloupe', 'EUR', 'GLP'], + ['GQ', 'Equatorial Guinea', 'XAF', 'GNQ'], + ['GR', 'Greece', 'EUR', 'GRC'], + ['GS', 'South Georgia and the South Sandwich Islands', 'GBP', 'SGS'], + ['GT', 'Guatemala', 'GTQ', 'GTM'], + ['GU', 'Guam', 'USD', 'GUM'], + ['GW', 'Guinea-Bissau', 'XOF', 'GNB'], + ['GY', 'Guyana', 'GYD', 'GUY'], + ['HK', 'Hong Kong', 'HKD', 'HKG'], + ['HM', 'Heard Island and McDonald Islands', 'AUD', 'HMD'], + ['HN', 'Honduras', 'HNL', 'HND'], + ['HR', 'Croatia', 'HRK', 'HRV'], + ['HT', 'Haiti', 'HTG', 'HTI'], + ['HU', 'Hungary', 'HUF', 'HUN'], + ['ID', 'Indonesia', 'IDR', 'IDN'], + ['IE', 'Ireland', 'EUR', 'IRL'], + ['IL', 'Israel', 'ILS', 'ISR'], + ['IM', 'Isle of Man', 'GBP', 'IMN'], + ['IN', 'India', 'INR', 'IND'], + ['IO', 'British Indian Ocean Territory', 'USD', 'IOT'], + ['IQ', 'Iraq', 'IQD', 'IRQ'], + ['IR', 'Iran', 'IRR', 'IRN'], + ['IS', 'Iceland', 'ISK', 'ISL'], + ['IT', 'Italy', 'EUR', 'ITA'], + ['JE', 'Jersey', 'GBP', 'JEY'], + ['JM', 'Jamaica', 'JMD', 'JAM'], + ['JO', 'Jordan', 'JOD', 'JOR'], + ['JP', 'Japan', 'JPY', 'JPN'], + ['KE', 'Kenya', 'KES', 'KEN'], + ['KG', 'Kyrgyzstan', 'KGS', 'KGZ'], + ['KH', 'Cambodia', 'KHR', 'KHM'], + ['KI', 'Kiribati', 'AUD', 'KIR'], + ['KM', 'Comoros', 'KMF', 'COM'], + ['KN', 'Saint Kitts and Nevis', 'XCD', 'KNA'], + ['KP', 'North Korea', 'KPW', 'PRK'], + ['KR', 'South Korea', 'KRW', 'KOR'], + ['KW', 'Kuwait', 'KWD', 'KWT'], + ['KY', 'Cayman Islands', 'KYD', 'CYM'], + ['KZ', 'Kazakhstan', 'KZT', 'KAZ'], + ['LA', 'Laos', 'LAK', 'LAO'], + ['LB', 'Lebanon', 'LBP', 'LBN'], + ['LC', 'Saint Lucia', 'XCD', 'LCA'], + ['LI', 'Liechtenstein', 'CHF', 'LIE'], + ['LK', 'Sri Lanka', 'LKR', 'LKA'], + ['LR', 'Liberia', 'LRD', 'LBR'], + ['LS', 'Lesotho', 'LSL', 'LSO'], + ['LT', 'Lithuania', 'LTL', 'LTU'], + ['LU', 'Luxembourg', 'EUR', 'LUX'], + ['LV', 'Latvia', 'LVL', 'LVA'], + ['LY', 'Libya', 'LYD', 'LBY'], + ['MA', 'Morocco', 'MAD', 'MAR'], + ['MC', 'Monaco', 'EUR', 'MCO'], + ['MD', 'Moldova', 'MDL', 'MDA'], + ['ME', 'Montenegro', 'EUR', 'MNE'], + ['MF', 'Saint Martin', 'EUR', 'MAF'], + ['MG', 'Madagascar', 'MGA', 'MDG'], + ['MH', 'Marshall Islands', 'USD', 'MHL'], + ['MK', 'Macedonia', 'MKD', 'MKD'], + ['ML', 'Mali', 'XOF', 'MLI'], + ['MM', 'Myanmar [Burma]', 'MMK', 'MMR'], + ['MN', 'Mongolia', 'MNT', 'MNG'], + ['MO', 'Macao', 'MOP', 'MAC'], + ['MP', 'Northern Mariana Islands', 'USD', 'MNP'], + ['MQ', 'Martinique', 'EUR', 'MTQ'], + ['MR', 'Mauritania', 'MRO', 'MRT'], + ['MS', 'Montserrat', 'XCD', 'MSR'], + ['MT', 'Malta', 'EUR', 'MLT'], + ['MU', 'Mauritius', 'MUR', 'MUS'], + ['MV', 'Maldives', 'MVR', 'MDV'], + ['MW', 'Malawi', 'MWK', 'MWI'], + ['MX', 'Mexico', 'MXN', 'MEX'], + ['MY', 'Malaysia', 'MYR', 'MYS'], + ['MZ', 'Mozambique', 'MZN', 'MOZ'], + ['NA', 'Namibia', 'NAD', 'NAM'], + ['NC', 'New Caledonia', 'XPF', 'NCL'], + ['NE', 'Niger', 'XOF', 'NER'], + ['NF', 'Norfolk Island', 'AUD', 'NFK'], + ['NG', 'Nigeria', 'NGN', 'NGA'], + ['NI', 'Nicaragua', 'NIO', 'NIC'], + ['NL', 'Netherlands', 'EUR', 'NLD'], + ['NO', 'Norway', 'NOK', 'NOR'], + ['NP', 'Nepal', 'NPR', 'NPL'], + ['NR', 'Nauru', 'AUD', 'NRU'], + ['NU', 'Niue', 'NZD', 'NIU'], + ['NZ', 'New Zealand', 'NZD', 'NZL'], + ['OM', 'Oman', 'OMR', 'OMN'], + ['PA', 'Panama', 'PAB', 'PAN'], + ['PE', 'Peru', 'PEN', 'PER'], + ['PF', 'French Polynesia', 'XPF', 'PYF'], + ['PG', 'Papua New Guinea', 'PGK', 'PNG'], + ['PH', 'Philippines', 'PHP', 'PHL'], + ['PK', 'Pakistan', 'PKR', 'PAK'], + ['PL', 'Poland', 'PLN', 'POL'], + ['PM', 'Saint Pierre and Miquelon', 'EUR', 'SPM'], + ['PN', 'Pitcairn Islands', 'NZD', 'PCN'], + ['PR', 'Puerto Rico', 'USD', 'PRI'], + ['PS', 'Palestine', 'ILS', 'PSE'], + ['PT', 'Portugal', 'EUR', 'PRT'], + ['PW', 'Palau', 'USD', 'PLW'], + ['PY', 'Paraguay', 'PYG', 'PRY'], + ['QA', 'Qatar', 'QAR', 'QAT'], + ['RE', 'Réunion', 'EUR', 'REU'], + ['RO', 'Romania', 'RON', 'ROU'], + ['RS', 'Serbia', 'RSD', 'SRB'], + ['RU', 'Russia', 'RUB', 'RUS'], + ['RW', 'Rwanda', 'RWF', 'RWA'], + ['SA', 'Saudi Arabia', 'SAR', 'SAU'], + ['SB', 'Solomon Islands', 'SBD', 'SLB'], + ['SC', 'Seychelles', 'SCR', 'SYC'], + ['SD', 'Sudan', 'SDG', 'SDN'], + ['SE', 'Sweden', 'SEK', 'SWE'], + ['SG', 'Singapore', 'SGD', 'SGP'], + ['SH', 'Saint Helena', 'SHP', 'SHN'], + ['SI', 'Slovenia', 'EUR', 'SVN'], + ['SJ', 'Svalbard and Jan Mayen', 'NOK', 'SJM'], + ['SK', 'Slovakia', 'EUR', 'SVK'], + ['SL', 'Sierra Leone', 'SLL', 'SLE'], + ['SM', 'San Marino', 'EUR', 'SMR'], + ['SN', 'Senegal', 'XOF', 'SEN'], + ['SO', 'Somalia', 'SOS', 'SOM'], + ['SR', 'Suriname', 'SRD', 'SUR'], + ['SS', 'South Sudan', 'SSP', 'SSD'], + ['ST', 'São Tomé and Príncipe', 'STD', 'STP'], + ['SV', 'El Salvador', 'USD', 'SLV'], + ['SX', 'Sint Maarten', 'ANG', 'SXM'], + ['SY', 'Syria', 'SYP', 'SYR'], + ['SZ', 'Swaziland', 'SZL', 'SWZ'], + ['TC', 'Turks and Caicos Islands', 'USD', 'TCA'], + ['TD', 'Chad', 'XAF', 'TCD'], + ['TF', 'French Southern Territories', 'EUR', 'ATF'], + ['TG', 'Togo', 'XOF', 'TGO'], + ['TH', 'Thailand', 'THB', 'THA'], + ['TJ', 'Tajikistan', 'TJS', 'TJK'], + ['TK', 'Tokelau', 'NZD', 'TKL'], + ['TL', 'East Timor', 'USD', 'TLS'], + ['TM', 'Turkmenistan', 'TMT', 'TKM'], + ['TN', 'Tunisia', 'TND', 'TUN'], + ['TO', 'Tonga', 'TOP', 'TON'], + ['TR', 'Turkey', 'TRY', 'TUR'], + ['TT', 'Trinidad and Tobago', 'TTD', 'TTO'], + ['TV', 'Tuvalu', 'AUD', 'TUV'], + ['TW', 'Taiwan', 'TWD', 'TWN'], + ['TZ', 'Tanzania', 'TZS', 'TZA'], + ['UA', 'Ukraine', 'UAH', 'UKR'], + ['UG', 'Uganda', 'UGX', 'UGA'], + ['UM', 'U.S. Minor Outlying Islands', 'USD', 'UMI'], + ['US', 'United States', 'USD', 'USA'], + ['UY', 'Uruguay', 'UYU', 'URY'], + ['UZ', 'Uzbekistan', 'UZS', 'UZB'], + ['VA', 'Vatican City', 'EUR', 'VAT'], + ['VC', 'Saint Vincent and the Grenadines', 'XCD', 'VCT'], + ['VE', 'Venezuela', 'VEF', 'VEN'], + ['VG', 'British Virgin Islands', 'USD', 'VGB'], + ['VI', 'U.S. Virgin Islands', 'USD', 'VIR'], + ['VN', 'Vietnam', 'VND', 'VNM'], + ['VU', 'Vanuatu', 'VUV', 'VUT'], + ['WF', 'Wallis and Futuna', 'XPF', 'WLF'], + ['WS', 'Samoa', 'WST', 'WSM'], + ['XK', 'Kosovo', 'EUR', 'XKX'], + ['YE', 'Yemen', 'YER', 'YEM'], + ['YT', 'Mayotte', 'EUR', 'MYT'], + ['ZA', 'South Africa', 'ZAR', 'ZAF'], + ['ZM', 'Zambia', 'ZMK', 'ZMB'], + ['ZW', 'Zimbabwe', 'ZWL', 'ZWE'], +]; + +foreach ($currencies as $currency) { + $value = Database::select( + "*", + $currencyTable, + [ + 'where' => ['country_code = ?' => $currency[0]], + ], + 'first' + ); + + if (!empty($value)) { + continue; + } + + Database::insert( + $currencyTable, + [ + 'country_code' => $currency[0], + 'country_name' => $currency[1], + 'iso_code' => $currency[2], + ] + ); +} + +$fieldlabel = 'buycourses_company'; +$fieldtype = '1'; +$fieldtitle = BuyCoursesPlugin::get_lang('Company'); +$fielddefault = ''; +$field_id = UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); + +$fieldlabel = 'buycourses_vat'; +$fieldtype = '1'; +$fieldtitle = BuyCoursesPlugin::get_lang('VAT'); +$fielddefault = ''; +$field_id = UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); + +$fieldlabel = 'buycourses_address'; +$fieldtype = '1'; +$fieldtitle = BuyCoursesPlugin::get_lang('Address'); +$fielddefault = ''; +$field_id = UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); diff --git a/plugin/buycourses/index.php b/plugin/buycourses/index.php new file mode 100644 index 000000000..fefacd452 --- /dev/null +++ b/plugin/buycourses/index.php @@ -0,0 +1,7 @@ +install(); diff --git a/plugin/buycourses/lang/brazilian.php b/plugin/buycourses/lang/brazilian.php new file mode 100644 index 000000000..19debc6ca --- /dev/null +++ b/plugin/buycourses/lang/brazilian.php @@ -0,0 +1,99 @@ +configurar pagamento - Defina a moeda com a qual você gostaria de vender seus cursos ou sessões"; +$strings['InstructionsStepThree'] = "Para configurar cursos para vender. Vá em: configurar cursos e preços."; +$strings['BuyCourses'] = "Comprar cursos"; +$strings['ConfigurationOfCoursesAndPrices'] = "Configurar cursos e preços "; +$strings['SalesReport'] = "Relatório de vendas"; +$strings['UserInformation'] = "Detalhes do comprador"; +$strings['PaymentMethods'] = "Métodos de pagamento"; +$strings['ConfirmOrder'] = "Confirmar pedido"; +$strings['PurchaseData'] = "Dados de Compra"; +$strings['bc_subject'] = "Confirmação de ordem do curso"; +$strings['PurchaseStatusX'] = "Estado de compra: %s"; +$strings['PendingReasonByTransfer'] = " Pendente Aguardando confirmação da transferência."; +$strings['CancelOrder'] = "Anular ordem"; +$strings['BankAccountInformation'] = "Detalhes da conta bancária"; +$strings['BankAccount'] = "Contas bancárias"; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Uma vez confirmada, você receberá um e-mail com os dados bancários e uma referência de ordem."; +$strings['SubscriptionToCourseXSuccessful'] = "Sua inscrição para \"%s\" foi concluída com sucesso."; +$strings['OrderCanceled'] = "Ordem cancelada"; +$strings['OrderStatus'] = "Status do pedido"; +$strings['SearchByStatus'] = "Pesquisar por estado"; +$strings['OrderReference'] = "Código de encomenda"; +$strings['OrderDate'] = "Data do pedido"; +$strings['ProductType'] = "Tipo de produto"; +$strings['SubscribeUser'] = "Inscrever utilizador"; +$strings['DeleteOrder'] = "Excluir a ordem"; +$strings['ErrorContactPlatformAdmin'] = "Por favor entre em contato com o administrador da plataforma de erro desconhecido.."; +$strings['PendingReasonByAddress'] = ". Pendente Você não digitou um endereço de entrega confirmado."; +$strings['PendingReasonByAuthorization'] = " Pendentes fundos Nós ainda não capturados.."; +$strings['PendingReasonByEcheck'] = " Pendente O pagamento foi feito por um eCheck que ainda não foi eliminada."; +$strings['PendingReasonByIntl'] = " Pendente Nós não temos um mecanismo de retirada de fundos.."; +$strings['PendingReasonByMulticurrency'] = " Pendente Nós não equilibrar na moeda enviado."; +$strings['PendingReasonByOrder'] = " Pendente Ordem feita Nós ainda não capturado fundos..."; +$strings['PendingReasonByPaymentReview'] = " Pendente O pagamento está sendo revisto pelo PayPal para o risco."; +$strings['PendingReasonByRegulatoryReview'] = " Pendente O pagamento está sendo revisado para conformidade com regulamentações governamentais.."; +$strings['PendingReasonByUnilateral'] = " Pendente O e-mail ainda não está registrado o confirmou.."; +$strings['PendingReasonByUpgrade'] = " Pendente O pagamento foi feito por cartão de crédito.."; +$strings['PendingReasonByVerify'] = " Pendente Desculpe Nós ainda não são verificados no PayPal..."; +$strings['PendingReasonByOther'] = " Pendente Por favor, entre em contato com o administrador da plataforma.."; +$strings['PayPalPaymentOKPleaseConfirm'] = "PayPal relata a transação está pronto para ser executado. Para confirmar que você está OK para prosseguir, clique no botão de confirmação abaixo. Uma vez clicado, você será registrado para o curso e os fundos serão transferido da sua conta PayPal para a nossa loja. Você sempre pode acessar seus cursos através da aba 'Meus cursos' Obrigado por seu costume.!"; +$strings['Sandbox'] = "Ambiente de teste"; +$strings['PayPalConfig'] = "Configuração PayPal:"; +$strings['TransfersConfig'] = "Configurar transferências bancárias:"; +$strings['PluginInstruction'] = "Você pode ativar ou desativar a opção de pagar via PayPal ou transferência bancária na seção de configuração do plugin."; +$strings['ClickHere'] = " Clique aqui para obter mais detalhes"; +$strings['CurrencyType'] = "Tipo de moeda"; +$strings['InfoCurrency'] = "Define a moeda para o pagamento de seus cursos."; +$strings['ApiUsername'] = "Nome de usuário API"; +$strings['ApiPassword'] = "Senha API"; +$strings['ApiSignature'] = "Assinatura API"; +$strings['InfoApiCredentials'] = "Para gerar as suas credenciais de API para integrar Chamilo com a sua conta PayPal, você deve seguir os seguintes passos"; +$strings['InfoApiStepOne'] = "Vá para a sua conta do PayPal, Resumo , então preferências do vendedor na Ferramentas de Vendas menu (se o fizer não ter este item de menu, você pode precisar de obter a autorização para vender o material através do PayPal em primeiro lugar)."; +$strings['InfoApiStepTwo'] = "No parágrafo Acesso API , clique em Atualizar "; +$strings['InfoApiStepThree'] = "Na opção 2 (credenciais de solicitação de API para criar o seu próprio nome de usuário e senha API), clique no botão Exibir API Assinatura link , e copiar as credenciais apresentadas à direita no formulário do plugin BuyCourses."; +$strings['ErrorOccurred'] = " Ocorreu um erro Código: %s. Mensagem: %s. Por favor, entre em contato com o Admin da plataforma."; +$strings['VisibleInCatalog'] = "Visível no catálogo"; +$strings['Beneficiaries'] = "Beneficiários"; +$strings['AvailableCourse'] = "Campo disponível"; +$strings['ShowOnCourseCatalog'] = "Mostrar no catálogo de cursos"; +$strings['ByStatus'] = "Por estado"; +$strings['ByUser'] = "Por usuário"; +$strings['ByEmail'] = "Por email"; +$strings['PaymentMethod'] = "Método de pagamento"; +$strings['SWIFT'] = "Código SWIFT"; +$strings['SWIFT_help'] = "Formato padrão de Códigos de Identificação Bancária (BIC) e serve como um identificador exclusivo de um banco ou instituição financeira"; +$strings['ExportReport'] = "Exportar Relatório de Vendas"; +$strings['OrderTime'] = "Hora do pedido"; +$strings['SelectDateRange'] = "Selecione uma data de início e uma data de término para o relatório"; diff --git a/plugin/buycourses/lang/dutch.php b/plugin/buycourses/lang/dutch.php new file mode 100644 index 000000000..d265f6c10 --- /dev/null +++ b/plugin/buycourses/lang/dutch.php @@ -0,0 +1,135 @@ + %s . Deze percentage zal invermindering gebracht worden van de totale prijs van het product. Het verschil in het bedrag zal aan de volgende commissies toegepast worden."; +$strings['CoursesInSessionsDoesntDisplayHere'] = "De cursussen die binnen een training zitten worden niet weergegeven in de lijst van de cursussen als afzonderlijke producten."; +$strings['WantToSellCourses'] = "Wilt u leren en wat geld verdienen met uw eigen cursussen? Deze lijst kan worden gevuld met de verkoop van uw cursussen via dit platform. Neem contact op met ons."; +$strings['SelectOptionToProceed'] = "Selecteer de optie om verder te gaan"; +$strings['VerifyTotalAmountToProceedPayout'] = "Controleer het totale bedrag voor over te gaan tot de betaling van commissies. Dit bedrag heeft geen extra kosten voor PayPal rekening houder. De cursussen verkopen die niet meetellen met een Paypal-account voor de begunstigden niet in aanmerking genomen."; +$strings['TotalAcounts'] = "Totaal van de rekeningen:"; +$strings['TotalPayout'] = "Totaal te betalen:"; +$strings['PayoutDate'] = "Betaaldatum:"; +$strings['CautionThisProcessCantBeCanceled'] = "Let op:Dit proces kan een paar minuten duren en kan niet worden geannuleerd."; +$strings['ProcessingPayoutsDontCloseThisWindow'] = "Verder gaan met betalingen. Dit venster niet sluiten totdat dit proces is afgerond."; +$strings['PayoutSuccess'] = "Betalingen met succes verwerkt"; +$strings['Buyer'] = "Koper"; +$strings['BankTransfer'] = "Bankoverschrijving"; +$strings['SaleInfo'] = "Verkoop Informatie"; +$strings['SaleStatusPending'] = "Verkoop in behandeling"; +$strings['SaleStatusCanceled'] = "Verkoop geannuleerd"; +$strings['SaleStatusCompleted'] = "verkoop afgerond"; +$strings['PayoutStatusPending'] = "Betaling in behandeling"; +$strings['PayoutStatusCanceled'] = "Betaling geannuleerd"; +$strings['PayoutStatusCompleted'] = "Betaling afgerond"; +$strings['PayoutsTotalPending'] = "Onafgehandelde betalingen:"; +$strings['PayoutsTotalCanceled'] = "Geannuleerde betalingen:"; +$strings['PayoutsTotalCompleted'] = "Afgeronde betalingen:"; +$strings['TotalAmount'] = "Totaalbedrag:"; +$strings['CourseListOnSale'] = "Lijst van cursussen in verkoop"; +$strings['AvailableCourses'] = "Beschikbare Cursussen"; +$strings['Price'] = "Prijs"; +$strings['SearchFilter'] = "Zoekmachine"; +$strings['MinimumPrice'] = "Minimuum prijs"; +$strings['MaximumPrice'] = "Maximuumm prijs"; +$strings['AvailableCoursesConfiguration'] = "Beschikbare cursussen overzicht"; +$strings['PaymentsConfiguration'] = "Betalingsoverzicht"; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "Je bent geregistreerd voor de cursus."; +$strings['SeeDescription'] = "Omschrijving"; +$strings['Buy'] = "Koop / inschrijven"; +$strings['WaitingToReceiveThePayment'] = "In afwachting van uw betaling"; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Je bent geregistreerd voor de sessie"; +$strings['ItemNotSaved'] = "Item niet opgeslagen"; +$strings['TitlePlugin'] = "Alles wat je nodig hebt om online cursussen te onderwijzen en te verkopen"; +$strings['PluginPresentation'] = "The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!"; +$strings['Instructions'] = "Instructies"; +$strings['InstructionsStepOne'] = "Maak een cursus of een sessie op het platform."; +$strings['InstructionsStepTwo'] = "In de betaling , stelt de valuta waarmee u wilt uw cursussen of sessies te verkopen."; +$strings['InstructionsStepThree'] = "Overzicht van cursussen om te verkopen in de Cursussen en prijzen Instellingen."; +$strings['BuyCourses'] = "Cursussen Kopen"; +$strings['ConfigurationOfCoursesAndPrices'] = "Cursussen en Prijzen overzicht"; +$strings['SalesReport'] = "Verkoopcijfers"; +$strings['UserInformation'] = "Gegevens van de koper"; +$strings['PaymentMethods'] = "Betaal methodes"; +$strings['ConfirmOrder'] = "Bevestig bestelling"; +$strings['PurchaseData'] = "Aankoopgegevens"; +$strings['bc_subject'] = "Bevestiging cursus order"; +$strings['PurchaseStatusX'] = "Aankoop status: %s"; +$strings['PendingReasonByTransfer'] = " In afwachting . In afwachting van de overdracht bevestiging"; +$strings['CancelOrder'] = "Annuleer order"; +$strings['BankAccountInformation'] = "Bankgegevens"; +$strings['BankAccount'] = "Bankrekening"; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Eenmaal bevestigd, ontvangt u een e-mail met de bankgegevens en een orderreferentie."; +$strings['SubscriptionToCourseXSuccessful'] = "Uw aankoop op \"%s\" werd met succes afgerond."; +$strings['OrderCanceled'] = "Order geannuleerd"; +$strings['OrderStatus'] = "Bestelstatus"; +$strings['PayoutStatus'] = "Betalingsstatus"; +$strings['SearchByStatus'] = "Zoeken op status"; +$strings['OrderReference'] = "Bestelreferentie"; +$strings['OrderDate'] = "Besteldatum"; +$strings['OrderPrice'] = "Bestel prijs"; +$strings['ProductType'] = "Artikel type"; +$strings['SubscribeUser'] = "Onderteken gebruiker"; +$strings['DeleteOrder'] = "Verwijder bestelling"; +$strings['ErrorContactPlatformAdmin'] = "Onbekende fout. Neem contact op met het platform administrator."; +$strings['PendingReasonByAddress'] = "in afwachting. Je hebt een bevestigde verzendadres niet in te voeren."; +$strings['PendingReasonByAuthorization'] = "in afwachting. We hebben nog geen betalingen."; +$strings['PendingReasonByEcheck'] = "in afwachting. De betaling is verricht door een eCheck die nog niet is vrijgemaakt"; +$strings['PendingReasonByIntl'] = "in afwachting.We hebben geen terugtrekking mechanisme voor betalingen."; +$strings['PendingReasonByMulticurrency'] = "in afwachting. We hebben niet in evenwicht te brengen in de valuta verzonden."; +$strings['PendingReasonByOrder'] = "in afwachting. Bestelling. We hebben nog geen betalingen."; +$strings['PendingReasonByPaymentReview'] = "in afwachting. De betaling wordt beoordeeld door PayPal voor risico."; +$strings['PendingReasonByRegulatoryReview'] = "in afwachting. De betaling wordt beoordeeld voor de naleving van regelgeving van de overheid."; +$strings['PendingReasonByUnilateral'] = "in afwachting. Het e-mailadres is nog niet geregistreerd of bevestigd."; +$strings['PendingReasonByUpgrade'] = "in afwachting. De betaling werd gedaan via credit card."; +$strings['PendingReasonByVerify'] = "in afwachting. Sorry. We zijn nog niet in Paypal geverifieerd."; +$strings['PendingReasonByOther'] = "in afwachting. Neem contact op met het platform admin."; +$strings['PayPalPaymentOKPleaseConfirm'] = "PayPal rapporteert de transactie nu worden gestart. Om te erkennen dat u op OK om verder te gaan, klikt u op de knop bevestiging hieronder. Eenmaal geklikt, wordt u ingeschreven voor de cursus en het geld zal naar onze winkel worden overgedragen van uw PayPal-rekening. U kunt altijd toegang tot uw cursussen via het tabblad 'Mijn cursussen'. Dank u voor uw aankoop"; +$strings['Sandbox'] = "Test omgeving"; +$strings['PayPalAccount'] = "Paypal rekening"; +$strings['NoPayPalAccountDetected'] = "Geen Paypal account gedetecteerd"; +$strings['PayPalConfig'] = "PayPal bevestiging:"; +$strings['TransfersConfig'] = "Bank overschrijving bevestiging:"; +$strings['PluginInstruction'] = "U kunt de optie in- of uitschakelen om te betalen via PayPal of overschrijving instellen in de configuratie sectie van de plugin."; +$strings['ClickHere'] = "Click here voor meer details"; +$strings['CurrencyType'] = "Valuta type"; +$strings['InfoCurrency'] = "Stel in de valuta voor de betaling van uw cursussen."; +$strings['ApiUsername'] = "API Gebruikersnaam"; +$strings['ApiPassword'] = "API Paswoord"; +$strings['ApiSignature'] = "API Handtekening"; +$strings['InfoApiCredentials'] = "To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps"; +$strings['InfoApiStepOne'] = "Ga naar je PayPal-account, Overzicht en verkoper voorkeuren in het Verkoop Tools menu (als je niet dit menu-item hebt, om spullen te verkopen moet u eerst via PayPal om de toestemming te krijgen )."; +$strings['InfoApiStepTwo'] = "In paragraaf API-toegang , klikt u op Bijwerken "; +$strings['InfoApiStepThree'] = "In optie 2 (API Request referenties aan uw eigen API gebruikersnaam en wachtwoord), klikt u op de Bekijk API Handtekening , en kopieer de getoonde rechtsaf de Cursusshop plugin vorm referenties"; +$strings['ErrorOccurred'] = " Er is een fout opgetreden . Code:% s. Bericht:% s. Neem contact op met naar platform admin"; +$strings['VisibleInCatalog'] = "Zichtbaar in catalogus"; +$strings['Beneficiaries'] = "begunstigden"; +$strings['AvailableCourse'] = "beschikbare cursus"; +$strings['ShowOnCourseCatalog'] = "Toon op de cursusoverzicht"; +$strings['ByStatus'] = "Door de status"; +$strings['ByUser'] = "Door de gebruiker"; +$strings['PaymentMethod'] = "Betaal methodes"; +$strings['SWIFT'] = "SWIFT code"; +$strings['SWIFT_help'] = "Standaardformaat van Bank Identifier Codes (BIC) en dient als een unieke identificatiecode voor een bank of financiële instelling"; diff --git a/plugin/buycourses/lang/english.php b/plugin/buycourses/lang/english.php new file mode 100644 index 000000000..deb6f0ae3 --- /dev/null +++ b/plugin/buycourses/lang/english.php @@ -0,0 +1,321 @@ + %s . This percentage will be discounted from the total product price. The difference will be the amount to apply the following commissions."; +$strings['CoursesInSessionsDoesntDisplayHere'] = "The courses which are inside a training session don't appear in the list of courses to configure as individual products."; +$strings['WantToSellCourses'] = "Do you want to teach and earn some money with your own courses? This list could be filled with your earnings selling courses through this platform. Get in touch with us."; +$strings['SelectOptionToProceed'] = "Select option to proceed"; +$strings['VerifyTotalAmountToProceedPayout'] = "Please check the total amount to proceed with the payment of commissions. This amount doesn't take extra costs for PayPal into account. The courses sales that do not count with a Paypal account for the beneficiaries will not be considered."; +$strings['TotalAcounts'] = "Total of accounts:"; +$strings['TotalPayout'] = "Total to be paid:"; +$strings['PayoutDate'] = "Payment date:"; +$strings['CautionThisProcessCantBeCanceled'] = "Caution: this process can take a few minutes and cannot be cancelled."; +$strings['ProcessingPayoutsDontCloseThisWindow'] = "Proceeding with payments. Don't close this window until this process is finalized."; +$strings['PayoutSuccess'] = "Payments processed successfully"; +$strings['Buyer'] = "Buyer"; +$strings['BankTransfer'] = "Bank transfer"; +$strings['SaleInfo'] = "Sale information"; +$strings['SaleStatusPending'] = "Sale pending"; +$strings['SaleStatusCanceled'] = "Sale canceled"; +$strings['SaleStatusCompleted'] = "Sale completed"; +$strings['PayoutStatusPending'] = "Payment pending"; +$strings['PayoutStatusCanceled'] = "Payment cancelled"; +$strings['PayoutStatusCompleted'] = "Payment completed"; +$strings['PayoutsTotalPending'] = "Pending payments:"; +$strings['PayoutsTotalCanceled'] = "Cancelled payments:"; +$strings['PayoutsTotalCompleted'] = "Completed payments:"; +$strings['Total'] = "Total"; +$strings['TotalAmount'] = "Total amount:"; +$strings['CourseListOnSale'] = "List of courses on sale"; +$strings['AvailableCourses'] = "Available Courses"; +$strings['Price'] = "Price"; +$strings['SearchFilter'] = "Search filter"; +$strings['MinimumPrice'] = "Minimum price"; +$strings['MaximumPrice'] = "Maximum price"; +$strings['AvailableCoursesConfiguration'] = "Available courses configuration"; +$strings['PaymentsConfiguration'] = "Payments configuration"; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "You are already registered in the course."; +$strings['SeeDescription'] = "Description"; +$strings['Buy'] = "Buy"; +$strings['WaitingToReceiveThePayment'] = "Currently pending payment"; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = "You are already registered in the session"; +$strings['ItemNotSaved'] = "Item not saved"; +$strings['TitlePlugin'] = "Everything you need to teach and sell courses online"; +$strings['PluginPresentation'] = "The BuyCourses Plugin gives you the means to sell your courses or sessions and teach online, through only a few simple steps and settings. What are you waiting for? Start selling courses through Chamilo LMS!"; +$strings['Instructions'] = "Instructions"; +$strings['InstructionsStepOne'] = "Create a course or session on the platform."; +$strings['InstructionsStepTwo'] = "In the payment settings, set the currency with which you would like to sell your courses or sessions."; +$strings['InstructionsStepThree'] = "Configure courses to sell in the Courses and prices settings."; +$strings['BuyCourses'] = "Buy courses"; +$strings['ConfigurationOfCoursesAndPrices'] = "Courses and prices configuration"; +$strings['SalesReport'] = "Sales report"; +$strings['UserInformation'] = "Buyer's details"; +$strings['PaymentMethods'] = "Payment methods"; +$strings['ConfirmOrder'] = "Confirm order"; +$strings['PurchaseData'] = "Purchase data"; +$strings['bc_subject'] = "Confirmation of course order"; +$strings['PurchaseStatusX'] = "Purchase status: %s"; +$strings['PendingReasonByTransfer'] = "Pending. Awaiting for transfer confirmation"; +$strings['CancelOrder'] = "Cancel order"; +$strings['BankAccountInformation'] = "Bank account details"; +$strings['BankAccount'] = "Bank account"; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Once confirmed, you will receive an e-mail with the bank details and an order reference."; +$strings['SubscriptionToCourseXSuccessful'] = "Completed process. Your subscription to \"%s\" was completed successfully."; +$strings['OrderCanceled'] = "Order canceled"; +$strings['OrderStatus'] = "Order status"; +$strings['PayoutStatus'] = "Payment status"; +$strings['SearchByStatus'] = "Search by status"; +$strings['OrderReference'] = "Order reference"; +$strings['OrderDate'] = "Order date"; +$strings['OrderPrice'] = "Order price"; +$strings['ProductType'] = "Product type"; +$strings['SubscribeUser'] = "Subscribe user"; +$strings['DeleteOrder'] = "Delete order"; +$strings['ErrorContactPlatformAdmin'] = "Unknown error. Please contact the platform administrator."; +$strings['PendingReasonByAddress'] = "Pending. You did not enter a confirmed shipping address."; +$strings['PendingReasonByAuthorization'] = "Pending. We have not yet captured funds."; +$strings['PendingReasonByEcheck'] = "Pending. The payment was made by an eCheck that has not yet cleared"; +$strings['PendingReasonByIntl'] = "Pending. We have no a withdrawal mechanism for funds."; +$strings['PendingReasonByMulticurrency'] = "Pending. We have not balance in the currency sent."; +$strings['PendingReasonByOrder'] = "Pending. Order made. We have not yet captured funds."; +$strings['PendingReasonByPaymentReview'] = "Pending. The payment is being reviewed by PayPal for risk."; +$strings['PendingReasonByRegulatoryReview'] = "Pending. The payment is being reviewed for compliance with government regulations."; +$strings['PendingReasonByUnilateral'] = "Pending. The email address is not yet registered o confirmed."; +$strings['PendingReasonByUpgrade'] = "Pending. The payment was made via credit card."; +$strings['PendingReasonByVerify'] = "Pending. Sorry. We are not yet verified in PayPal."; +$strings['PendingReasonByOther'] = "Pending. Please contact with the platform admin."; +$strings['PayPalPaymentOKPleaseConfirm'] = "PayPal reports the transaction is ready to be executed. To acknowledge that you are OK to proceed, please click the confirmation button below. Once clicked, you will be registered to the course and the funds will be transferred from your PayPal account to our shop. You can always access your courses through the 'My courses' tab. Thank you for your custom!"; +$strings['Sandbox'] = "Test environment"; +$strings['PayPalAccount'] = "Paypal account"; +$strings['NoPayPalAccountDetected'] = "No Paypal account detected"; +$strings['PayPalConfig'] = "PayPal configuration:"; +$strings['TransfersConfig'] = "Bank transfers configuration:"; +$strings['PluginInstruction'] = "You can enable or disable the option to pay via PayPal, Culqi or bank transfer settings in the configuration section of the plugin."; +$strings['ClickHere'] = "Click here for more details"; +$strings['CurrencyType'] = "Currency type"; +$strings['InfoCurrency'] = "Sets the currency for the payment of your courses."; +$strings['ApiUsername'] = "API Username"; +$strings['ApiPassword'] = "API Password"; +$strings['ApiSignature'] = "API Signature"; +$strings['InfoApiCredentials'] = "To generate your API credentials to integrate Chamilo with your PayPal account, you must follow the following steps"; +$strings['InfoApiStepOne'] = "Go to your PayPal account, Summary, then Seller preferences in the Selling Tools menu (if you do not have this menu entry, you might need to get the authorization to sell stuff through PayPal first)."; +$strings['InfoApiStepTwo'] = "In paragraph API access, click Update"; +$strings['InfoApiStepThree'] = "In Option 2 (Request API credentials to create your own API username and password), click the View API Signature link, and copy the credentials shown right into the BuyCourses plugin form"; +$strings['ErrorOccurred'] = "An error ocurred. Code: %s. Message: %s. Please contact to platform admin"; +$strings['VisibleInCatalog'] = "Visible in catalog"; +$strings['Beneficiaries'] = "Beneficiaries"; +$strings['AvailableCourse'] = "Available course"; +$strings['ShowOnCourseCatalog'] = "Show on course catalog"; +$strings['ByStatus'] = "By status"; +$strings['ByUser'] = "By user"; +$strings['ByEmail'] = "By email"; +$strings['PaymentMethod'] = "Payment method"; +$strings['SWIFT'] = "SWIFT code"; +$strings['SWIFT_help'] = "Standard format of Bank Identifier Codes (BIC) and serves as a unique identifier for a bank or financial institution"; +$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = "Please select your favorite payment method before confirming your order"; +$strings['NoPaymentOptionAvailable'] = 'No payment option available. Please report to the administrator.'; +$strings['XIsOnlyPaymentMethodAvailable'] = '%s is the only payment method available for this purchase.'; +$strings['hide_free_text'] = "Hide 'Free' text"; +$strings['culqi_enable'] = "Enable culqi"; +$strings['include_services'] = "Include services"; +$strings['CurrencyIsNotConfigured'] = "Please, configure a currency before continuing."; +$strings['Services'] = "Services"; +$strings['Service'] = "Service"; +$strings['NewService'] = "New service"; +$strings['ServiceName'] = "Service name"; +$strings['AppliesTo'] = "Applies to"; +$strings['ServiceInformation'] = "Service information"; +$strings['ListOfServicesOnSale'] = "List of services on sale"; +$strings['GlobalConfig'] = "Global configuration"; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Write here the terms and conditions of your e-commerce"; +$strings['EditService'] = "Edit service"; +$strings['DeleteThisService'] = "Delete this service"; +$strings['IConfirmIReadAndAcceptTermsAndCondition'] = "I confirm I read and accept the terms and conditions"; +$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Please select the correct info to apply the service"; +$strings['SaleStatusCancelled'] = "Sale cancelled"; +$strings['ServiceSaleInfo'] = "Service sale info"; +$strings['ServiceId'] = "Service Id"; +$strings['BoughtBy'] = "Bought by"; +$strings['PurchaserUser'] = "Purchaser user"; +$strings['Pending'] = "Pending"; +$strings['Names'] = "Names"; +$strings['SellerName'] = "Seller name"; +$strings['SellerId'] = "Seller id"; +$strings['SellerAddress'] = "Seller address"; +$strings['SellerEmail'] = "Seller e-mail"; +$strings['NextNumberInvoice'] = "Next invoice number"; +$strings['NextNumberInvoiceDescription'] = "Number of the following invoice"; +$strings['InvoiceSeries'] = "Invoice series"; +$strings['InvoiceSeriesDescription'] = "Optional parameter: Example invoice Id <series><year>/<number>"; +$strings['InvoiceView'] = "View invoice"; +$strings['NoInvoiceEnable'] = "No invoicing block enable"; +$strings['Company'] = "Company"; +$strings['VAT'] = "VAT"; +$strings['Address'] = "Address"; +$strings['InvoiceNumber'] = "Invoice number"; +$strings['InvoiceDate'] = "Invoice date"; +$strings['Invoice'] = "Invoice"; +$strings['SaleEmail'] = "Sales e-mail"; +$strings['PurchaseDetailsIntro'] = "Purchase details"; +$strings['PurchaseDetailsEnd'] = "Regards"; +$strings['ProductName'] = "Product name"; +$strings['BankAccountIntro'] = "Bank Account Info"; +$strings['AdditionalInfoRequired'] = 'More information needed'; +$strings['SubscriptionToServiceXSuccessful'] = "Subscription to service %s completed."; +$strings['ClickHereToFinish'] = "Click here to finish"; +$strings['OrderCancelled'] = "Order cancelled"; +$strings['use_currency_symbol'] = "Use currency symbol"; +$strings['ExportReport'] = "Export Sales Report"; +$strings['OrderTime'] = "Order time"; +$strings['SelectDateRange'] = "Select a start date and end date for the report"; +$strings['ServiceAdded'] = "Service added"; +$strings['ServiceEdited'] = "Service updated"; +$strings['ListOfServicesOnSale'] = "List of services for sale"; +$strings['AdditionalInfo'] = "Additional information"; +$strings['culqi_enable'] = "Enable Culqi"; +$strings['CulqiConfig'] = "Culqi configuration:"; +$strings['InfoCulqiCredentials'] = "To obtain your credentials, you will need to create an account on Culqi and enter the development mode, copy the merchant code in your dashboard, then enter the API Keys section and copy the corresponding key to paste it here."; +$strings['CommerceCode'] = "Merchant code"; +$strings['NoTermsAndConditionsProvided'] = "No defined terms and conditions"; +$strings['GlobalConfig'] = "Global configuration:"; +$strings['MyServices'] = "My services"; +$strings['SalePrice'] = "Sale price"; +$strings['YouNeedToBeRegisteredInAtLeastOneCourse'] = "You need to be registered in at least one course"; +$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = "You need to be registered in at least one session"; +$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "To obtain the certificate and/or the skills associated to this course, you need to buy the Certificate service. Go to the services catalogue to buy it by clicking here"; +$strings['ServiceDeleted'] = 'Service deleted'; +$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'The courses to which you are subscribed need at least one learning path that contains a final certificate item'; +$strings['GlobalTaxPerc'] = "Global tax rate"; +$strings['GlobalTaxPercDescription'] = "Default tax rate that will be used unless there is a specific tax rate for the course, session or service."; +$strings['TaxPerc'] = "Tax rate"; +$strings['TaxPercDescription'] = "If left blank, the global tax rate will be used."; +$strings['ByDefault'] = "by default (global value)"; +$strings['OnlyCourses'] = "Only courses"; +$strings['OnlySessions'] = "Only sessions"; +$strings['OnlyServices'] = "Only services"; +$strings['TaxAppliesTo'] = "Tax applied to"; +$strings['AllCoursesSessionsAndServices'] = "All (courses, sessions and services)"; +$strings['TaxNameCustom'] = "Tax name"; +$strings['TaxNameExamples'] = "VAT, IVA, IGV, TVA, IV ..."; +$strings['ErrorUpdateFieldDB'] = "Error updating the database fields"; +$strings['tpv_redsys_enable'] = "Enable RedSys POS"; +$strings['tpv_redsys_enable_help'] = "In order to use the RedSys POS payment method, it is necessary to download the \"REST INTEGRATION - PHP API \" files at the following link web de RedSys and locate the file apiRedSys.php in the plugin/buycourses/resources directory."; +$strings['NotFindRedsysFile'] = "The apiRedsys.php file cannot be found in plugin/buycourses/resources directory"; +$strings['TpvPayment'] = "POS payment"; +$strings['TpvRedsysConfig'] = "Redsys POS configuration"; +$strings['DS_MERCHANT_MERCHANTCODE'] = "Trade number (FUC)"; +$strings['DS_MERCHANT_TERMINAL'] = "Terminal number"; +$strings['DS_MERCHANT_CURRENCY'] = "Terminal currency"; +$strings['kc'] = "Secret encryption key"; +$strings['url_redsys'] = "Redsys connection URL"; +$strings['url_redsys_sandbox'] = "Redsys connection URL (Sandbox)"; +$strings['InfoTpvRedsysApiCredentials'] = "You must complete the following form fields with the information provided by the Redsys POS Technical Support:"; +$strings['InfoEmailExtra'] = "Extra info in payment e-mail"; +$strings['Coupon'] = "Coupon"; +$strings['DiscountAmount'] = "Discount amount"; +$strings['RedeemCoupon'] = "Redeem"; +$strings['NeedToAddCouponCode'] = "A coupon code has not been entered"; +$strings['CouponNotValid'] = "This coupon is not valid"; +$strings['CouponRedeemed'] = "Coupon redeemed"; +$strings['CouponDiscount'] = "Discount"; +$strings['CouponStatus'] = "Coupon status"; +$strings['CouponPercentage'] = "Percentage"; +$strings['CouponAmount'] = "Amount"; +$strings['CouponList'] = "Coupon list"; +$strings['CouponCode'] = "Coupon code"; +$strings['CouponDiscountType'] = "Discount type"; +$strings['CouponDateStart'] = "Valid from"; +$strings['CouponDateEnd'] = "Expires on"; +$strings['CouponDelivered'] = "Delivered"; +$strings['CouponDisable'] = "Disable"; +$strings['CouponEnable'] = "Enable"; +$strings['CouponCodeUsed'] = "Coupon code already used"; +$strings['CouponNoExists'] = "Coupon no exists"; +$strings['CouponErrorInsert'] = "Error inserting new coupon"; +$strings['CouponActive'] = "Active"; +$strings['CouponDisabled'] = "Disabled"; +$strings['CouponUpdate'] = "Coupon updated"; +$strings['CouponsConfiguration'] = "Coupons configuration"; +$strings['CouponAdd'] = "New coupon"; +$strings['ConfigureCoupon'] = "Configure coupon"; +$strings['DiscountCoupon'] = "Discount coupons"; +$strings['CouponsCode'] = "Code"; +$strings['DoYouHaveACoupon'] = "Do you have a coupon?"; +$strings['stripe_enable'] = "Enable Stripe"; +$strings['StripeConfig'] = "Stripe configuration:"; +$strings['InfoStripeCredentials'] = "To obtain the credentials you must first create an account in Stripe, copy the account id from your profile, go to the API Keys configuration section and copy the secret key, finally you must go to the Developers button (top right), register a new Endpoint in the Webhooks section, pointing to https://{site}/plugin/buycourses/src/stripe_response.php for the checkout.session.completed event type and copy the secret of the Endpoint."; +$strings['StripeAccountId'] = "Account id:"; +$strings['StripeSecret'] = "Secret key:"; +$strings['StripeEndpointSecret'] = "Endpoint secret:"; +$strings['PendingReasonByStripe'] = "Pending. Awaiting for payment confirmation ..."; +$strings['cecabank_enable'] = "Enable Cecabank POS"; +$strings['TpvCecabank'] = "POS payment (Cecabank)"; +$strings['CecaSecret'] = "Secret"; +$strings['CecaUrl'] = "Url"; +$strings['CecaMerchanId'] = "Merchant"; +$strings['CecaAcquirerId'] = "Adquirer"; +$strings['CecaTerminalId'] = "Terminal"; +$strings['CecaCypher'] = "Cypher"; +$strings['CecaCurrency'] = "Currency"; +$strings['CecaExponent'] = "Exponent"; +$strings['CecaSupportedPayment'] = "Payment supported"; +$strings['CecabankConfig'] = "Cecabank configuration"; +$strings['Country'] = "Country"; +$strings['PaymentType'] = "Payment type"; +$strings['CountryRelPaymentConfig'] = "Payment type configuration for the country"; +$strings['CountryRelPaymentMessage'] = "To process an order, the type of payment per country must be defined, otherwise the order will not be allowed to go through."; +$strings['CountryEmpty'] = "To process an order, you need to define the Country field in the user profile."; +$strings['Duration'] = "Duration"; +$strings['SubscriptionAdd'] = "Add subscription"; +$strings['SubscriptionList'] = "Subscription list"; +$strings['SubscriptionListOnSale'] = "Subscriptions on sale"; +$strings['SelectSubscription'] = "Select duration"; +$strings['SubscriptionNotValid'] = "Subscription not valid"; +$strings['SubscriptionSalesReport'] = "Sales report"; +$strings['BuySubscriptions'] = "Buy subscriptions"; +$strings['ConfigurationOfSubscriptionsAndPrices'] = "Subscriptions and prices configuration"; +$strings['FrequencyConfig'] = "Duration config"; +$strings['Subscriptions'] = "Subscriptions"; +$strings['HasSubscriptions'] = "Has subscriptions"; +$strings['FrequencyRemoved'] = "Period removed"; +$strings['SubscriptionPeriodOnUse'] = "Subscription period in use"; +$strings['FrequencyNotExits'] = "Period does not exist"; +$strings['FrequencyIncorrect'] = "Period incorrect"; +$strings['SubscriptionFrequencyValueDays'] = "Value on days"; +$strings['FrequencyNotUpdated'] = "Period not updated"; +$strings['FrequencyNotSaved'] = "Period not saved"; +$strings['NeedToAddDuration'] = "Need to add duration"; +$strings['SubscriptionNotValid'] = "Subscription not valid"; +$strings['SelecSubscription'] = "Select a subscription"; +$strings['ConfigureSubscriptionsFrequencies'] = "Configure subscriptions periods"; +$strings['FrequencyAdd'] = "Add periods"; +$strings['SubscriptionAlreadyExists'] = "Subscription already exists"; +$strings['SubscriptionPeriodDuration'] = "Subscription duration (in days)"; +$strings['Product'] = "Product name"; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = "Your product is now activated and you can now have access to it."; +$strings['hide_shopping_cart_from_course_catalogue'] = "Hide shopping cart from the course catalogue and leave the subscribe button"; diff --git a/plugin/buycourses/lang/french.php b/plugin/buycourses/lang/french.php new file mode 100644 index 000000000..a8eebb865 --- /dev/null +++ b/plugin/buycourses/lang/french.php @@ -0,0 +1,250 @@ + %s . Ce pourcentage sera soustrait du prix total du produit. La différence sera répartie selon les commissions définies ci-dessous."; +$strings['CoursesInSessionsDoesntDisplayHere'] = "Les cours qui se trouvent dans une session n'apparaissent pas dans la liste de cours individuels."; +$strings['WantToSellCourses'] = "Envie d'enseigner et de gagner un peu d'argent avec vos propres cours? Cette liste pourraît être pleine de ventes de vos cours. Contactez-nous."; +$strings['SelectOptionToProceed'] = "Sélectionnez une option pour continuer"; +$strings['VerifyTotalAmountToProceedPayout'] = "Veuillez vérifier le montant total avant de continuer le paiement des commissions. Ce montant ne prend pas en compte le coût additionnel éventuellement perçu par PayPal. Les ventes de cours pour lesquelles les bénéficiaires n'ont pas de compte PayPal configuré ne seront pas prises en compte."; +$strings['TotalAcounts'] = "Total des comptes:"; +$strings['TotalPayout'] = "Total à payer:"; +$strings['PayoutDate'] = "Date de paiement:"; +$strings['CautionThisProcessCantBeCanceled'] = "Attention: ce processus peut prendre quelques minutes et ne peut pas être interrompu."; +$strings['ProcessingPayoutsDontCloseThisWindow'] = "Exécution des paiements. Ne fermez pas cette fenêtre avant que le processus ne soit finalisé."; +$strings['PayoutSuccess'] = "Paiements effectués avec succès"; +$strings['Buyer'] = "Acheteur"; +$strings['BankTransfer'] = "Transfert bancaire"; +$strings['SaleInfo'] = "Information vente"; +$strings['SaleStatusPending'] = "Vente non confirmée"; +$strings['SaleStatusCanceled'] = "Vente annulée"; +$strings['SaleStatusCompleted'] = "Vente finalisée"; +$strings['PayoutStatusPending'] = "Paiement en attente"; +$strings['PayoutStatusCanceled'] = "Paiement annulé"; +$strings['PayoutStatusCompleted'] = "Paiement complété"; +$strings['PayoutsTotalPending'] = "Paiements en attente:"; +$strings['PayoutsTotalCanceled'] = "Paiements annulés:"; +$strings['PayoutsTotalCompleted'] = "Paiements complétés:"; +$strings['TotalAmount'] = "Montant total:"; +$strings['CourseListOnSale'] = "Liste de cours en vente"; +$strings['AvailableCourses'] = "Cours disponibles"; +$strings['Price'] = "Prix"; +$strings['SearchFilter'] = "Filtre de recherche"; +$strings['MinimumPrice'] = "Prix minimum"; +$strings['MaximumPrice'] = "Prix maximum"; +$strings['AvailableCoursesConfiguration'] = "Configuration des cours disponibles"; +$strings['PaymentsConfiguration'] = "Configuration des paiements"; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "L'utilisateur est déjà inscrit au cours"; +$strings['SeeDescription'] = "Voir description"; +$strings['Buy'] = "Acheter"; +$strings['WaitingToReceiveThePayment'] = "En attente de réception du paiement"; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Vous êtes déjà inscrit à cette session"; +$strings['ItemNotSaved'] = "Article non sauvé"; +$strings['TitlePlugin'] = "Tout le nécessaire pour enseigner et vendre des cours en ligne"; +$strings['PluginPresentation'] = "Le plugin BuyCourses vous donne les moyens nécessaires pour vendre vos cours ou vos sessions existants et d'enseigner en ligne, en quelques étapes très simples. Qu'attendez-vous? Commencez à vendre des cours grâce à Chamilo dès maintenant!"; +$strings['Instructions'] = "Instructions d'utilisation"; +$strings['InstructionsStepOne'] = "Créer un cours ou une session de formation sur la plateforme."; +$strings['InstructionsStepTwo'] = "Dans la section configuration de paiements, configurer le type de devise dans lequel vous souhaitez vendre vos cours et sessions"; +$strings['InstructionsStepThree'] = "Configurer les cours à vendre dans la section Configuration de cours et prix"; +$strings['BuyCourses'] = "Acheter des cours"; +$strings['ConfigurationOfCoursesAndPrices'] = "Configuration des cours et prix"; +$strings['SalesReport'] = "Rapport des ventes"; +$strings['UserInformation'] = "Fiche acheteur"; +$strings['PaymentMethods'] = "Méthodes de paiement"; +$strings['ConfirmOrder'] = "Confirmer commande"; +$strings['PurchaseData'] = "Détails d'achat"; +$strings['bc_subject'] = "Confirmation de commande de cours"; +$strings['PurchaseStatusX'] = "État de la vente: %s"; +$strings['PendingReasonByTransfer'] = "En attente. Transfert pas encore confirmé"; +$strings['CancelOrder'] = "Annuler la commande"; +$strings['BankAccountInformation'] = "Détails du compte bancaire"; +$strings['BankAccount'] = "Compte bancaire"; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Une fois confirmée, vous recevrez un e-mail avec les données bancaires et la référence de la commande"; +$strings['SubscriptionToCourseXSuccessful'] = "Votre inscription au \"%s\" est terminée."; +$strings['OrderCanceled'] = "Commande annulée"; +$strings['OrderStatus'] = "Statut de commande"; +$strings['PayoutStatus'] = "État du paiement"; +$strings['SearchByStatus'] = "Recherche par statut"; +$strings['OrderReference'] = "Référence de la commande"; +$strings['OrderDate'] = "Date de commande"; +$strings['OrderPrice'] = "Commande Prix"; +$strings['ProductType'] = "Type de produit"; +$strings['SubscribeUser'] = "Inscrire utilisateur"; +$strings['DeleteOrder'] = "Éliminer la commande"; +$strings['ErrorContactPlatformAdmin'] = "Une erreur inconnue s'est produite. Veuillez contacter l'administrateur de la plateforme."; +$strings['PendingReasonByAddress'] = "En attente. Il manque une adresse de livraison."; +$strings['PendingReasonByAuthorization'] = "En attente. Nous n'avons pas confirmé la réception des fonds."; +$strings['PendingReasonByEcheck'] = "En attente. Le paiement a été fait via un chèque qui n'a pas encore été libéré"; +$strings['PendingReasonByIntl'] = "En attente. Aucun moyen de toucher les fonds."; +$strings['PendingReasonByMulticurrency'] = "En attente. Nous n'avons pas de compte dans la devise indiquée."; +$strings['PendingReasonByOrder'] = "En attente. Commande enregistrée. Les fonds n'ont pas encore été libérés."; +$strings['PendingReasonByPaymentReview'] = "En attente. Le paiement est en révision anti-fraude chez PayPal."; +$strings['PendingReasonByRegulatoryReview'] = "En attente. Le paiement est actuellement revu pour correspondre aux règles mises en vigueur par le gouvernement."; +$strings['PendingReasonByUnilateral'] = "En attente. L'adresse e-mail n'est pas encore confirmée ou enregistrée."; +$strings['PendingReasonByUpgrade'] = "En attente. Le paiement a été fait par carte de crédit."; +$strings['PendingReasonByVerify'] = "En attente. Désolé, nous n'avons pas encore vérifié sur PayPal."; +$strings['PendingReasonByOther'] = "En attente. Veuillez contacter l'administrateur."; +$strings['PayPalPaymentOKPleaseConfirm'] = "PayPal nous indique que la transaction est prête à être exécutée. Par mesure de sécurité, nous vous demandons de bien vouloir confirmer une dernière fois la transaction en cliquant sur le bouton de confirmation ci-dessous. Une fois cliqué, vous serez immédiatement enregistré au cours, et les fonds correspondants seront soustraits de votre compte PayPal. Vous pouvez accéder à vos cours à tout moment à partir de l'onglet 'Mes cours'. Merci de votre fidélité!"; +$strings['Sandbox'] = "Environnement de test"; +$strings['PayPalAccount'] = "Compte Paypal"; +$strings['NoPayPalAccountDetected'] = "Pas de compte paypal détecté"; +$strings['PayPalConfig'] = "Configuration PayPal:"; +$strings['TransfersConfig'] = "Configuration des transfers bancaires:"; +$strings['PluginInstruction'] = "Vous pouvez activer ou désactiver l'option de paiements via PayPal, Culqi ou de transferts bancaires dans la section de configuration des plugins."; +$strings['ClickHere'] = "Cliquez ici pour plus d'infos"; +$strings['CurrencyType'] = "Type de devise"; +$strings['InfoCurrency'] = "Permet de configurer la devise pour l'achat des cours."; +$strings['ApiUsername'] = "Nom d'utilisateur de l'API"; +$strings['ApiPassword'] = "Mot de passe de l'API"; +$strings['ApiSignature'] = "Signature de l'API"; +$strings['InfoApiCredentials'] = "Pour générer vos données API pour intégrer votre compte PayPal à Chamilo, il vous faudra suivre les étapes suivantes"; +$strings['InfoApiStepOne'] = "Aller dans l'option profil de PayPal, et ensuite dans Outils de vente"; +$strings['InfoApiStepTwo'] = "Dans la section API d'accès, cliquer sur l'option Mettre à jour"; +$strings['InfoApiStepThree'] = "Dans l'option 2 de Configuration des données et permissions API, cliquer sur Voir signature API. Copier ces donées dans le formulaire de configuration de ce plugin"; +$strings['ErrorOccurred'] = "Une erreur est survenue. Code: %s. Message: %s. Veuillez contacter l'administrateur de la plateforme"; +$strings['VisibleInCatalog'] = "Visible dans le catalogue"; +$strings['Beneficiaries'] = "Bénéficiaires"; +$strings['AvailableCourse'] = "Cours disponibles"; +$strings['ShowOnCourseCatalog'] = "Afficher dans le catalogue de cours"; +$strings['ByStatus'] = "Par statut"; +$strings['ByUser'] = "Par utilisateur"; +$strings['ByEmail'] = "Par email"; +$strings['PaymentMethod'] = "Méthodes de paiement"; +$strings['SWIFT'] = "Code SWIFT"; +$strings['SWIFT_help'] = "Format standard des codes d'identification de banque (BIC) et sert un identifiant unique pour une banque ou une institution financière."; +$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = 'Veuillez sélectionner votre méthode de paiement préférée avant de confirmer'; +$strings['NoPaymentOptionAvailable'] = 'Aucune méthode de paiement disponible. Merci de bien vouloir rapporter ce problème à l\'administrateur.'; +$strings['XIsOnlyPaymentMethodAvailable'] = '%s est la seule méthode de paiement disponible pour cet achat.'; +$strings['public_main_menu_tab'] = "Montrer l'onglet dans le menu principal aux utilisateurs anonyme également"; +$strings['culqi_enable'] = "Activé culqi"; +$strings['include_services'] = "Inclure les services"; +$strings['hide_free_text'] = "Cacher le texte 'Free' ou 'Gratuit'"; +$strings['Services'] = "Services"; +$strings['ServiceName'] = "Nom du service"; +$strings['AppliesTo'] = "S'applique à"; +$strings['ListOfServicesOnSale'] = "Liste de services en vente"; +$strings['GlobalConfig'] = "Configuration globale"; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Écrire les conditions de ventes de votre e-commerce"; +$strings['NewService'] = "Nouveau service"; +$strings['Service'] = "Service"; +$strings['ServiceInformation'] = "Information sur le service"; +$strings['EditService'] = "Edition du service"; +$strings['DeleteThisService'] = "Supprimer ce service"; +$strings['IConfirmIReadAndAcceptTermsAndCondition'] = "Je confirme que j'ai lu et que j'accepte les termes et conditions"; +$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Veuillez choisir l'information à appliquer au service"; +$strings['SaleStatusCancelled'] = "Vente annulée"; +$strings['ServiceSaleInfo'] = "Information de vente"; +$strings['ServiceId'] = "Id du service"; +$strings['BoughtBy'] = "Acheté par"; +$strings['PurchaserUser'] = "Utilisateur acheteur"; +$strings['Pending'] = "En attente"; +$strings['Names'] = "Nom"; +$strings['ExportReport'] = "Export du rapport des ventes"; +$strings['OrderTime'] = "Heure de commande"; +$strings['SelectDateRange'] = "Sélectionnez une date de début et une date de fin pour le rapport"; +$strings['InfoEmailExtra'] = "Informations additionnelles dans l'e-mail de paiement"; +$strings['Coupon'] = "Coupon"; +$strings['DiscountAmount'] = "Ristourne totale"; +$strings['RedeemCoupon'] = "Échanger"; +$strings['NeedToAddCouponCode'] = "Aucun code de coupon n'a été introduit"; +$strings['CouponNotValid'] = "Ce coupon n'est pas valable"; +$strings['CouponRedeemed'] = "Coupon échangé"; +$strings['CouponDiscount'] = "Ristourne"; +$strings['CouponStatus'] = "Statut du coupon"; +$strings['CouponPercentage'] = "Pourcentage"; +$strings['CouponAmount'] = "Montant"; +$strings['CouponList'] = "Liste des coupons"; +$strings['CouponCode'] = "Code du coupon"; +$strings['CouponDiscountType'] = "Type de ristourne"; +$strings['CouponDateStart'] = "Valable apd"; +$strings['CouponDateEnd'] = "Expire le"; +$strings['CouponDelivered'] = "Livré"; +$strings['CouponDisable'] = "Désactivé"; +$strings['CouponEnable'] = "Activé"; +$strings['CouponCodeUsed'] = "Code de coupon déjà utilisé"; +$strings['CouponNoExists'] = "Ce coupon n'existe pas"; +$strings['CouponErrorInsert'] = "Erreur lors de l'insertion du coupon"; +$strings['CouponActive'] = "Actif"; +$strings['CouponDisabled'] = "Désactivé"; +$strings['CouponUpdate'] = "Coupon mis à jour"; +$strings['CouponsConfiguration'] = "Configuration coupons"; +$strings['CouponAdd'] = "Nouveau coupon"; +$strings['ConfigureCoupon'] = "Configurer le coupon"; +$strings['DiscountCoupon'] = "Coupons de ristourne"; +$strings['CouponsCode'] = "Code"; +$strings['DoYouHaveACoupon'] = "Vous avez un coupon?"; +$strings['stripe_enable'] = "Activer Stripe"; +$strings['StripeConfig'] = "Configuration de Stripe:"; +$strings['InfoStripeCredentials'] = "Pour obtenir les données de connexion, vous devez d'abord réer un compte Stripe (ce qui prend un certain temps), puis copier l'ID du compte depuis votre profil et le renseigner ici. Puis aller dans la configuration des clefs API et copier la clef secrète. Enfin, suivre le bouton 'Développeurs' (haut droite), enregistrer un nouveau Endpoint dans la section Webhooks, le pointer vers l'URL https://{site}/plugin/buycourses/src/stripe_response.php et l'associer à l'événement checkout.session.completed et copier le secret du Endpoint ici."; +$strings['StripeAccountId'] = "ID du compte:"; +$strings['StripeSecret'] = "Clef secrète:"; +$strings['StripeEndpointSecret'] = "Clef secrète du Endpoint:"; +$strings['PendingReasonByStripe'] = "Un instant.... En attente de confirmation du paiement..."; +$strings['cecabank_enable'] = "Activer Cecabank POS"; +$strings['TpvCecabank'] = "Paiement POS (Cecabank)"; +$strings['CecaSecret'] = "Clef secrète"; +$strings['CecaUrl'] = "URL"; +$strings['CecaMerchanId'] = "Marchand"; +$strings['CecaAcquirerId'] = "Acquéreur"; +$strings['CecaTerminalId'] = "Terminal"; +$strings['CecaCypher'] = "Chiffrage"; +$strings['CecaCurrency'] = "Devise"; +$strings['CecaExponent'] = "Exposant"; +$strings['CecaSupportedPayment'] = "Paiement supporté"; +$strings['CecabankConfig'] = "Configuration de Cecabank"; +$strings['Country'] = "Pays"; +$strings['PaymentType'] = "Type de paiement"; +$strings['CountryRelPaymentConfig'] = "Configuration du type de paiement par pays"; +$strings['CountryRelPaymentMessage'] = "Pour faire une commande, il est nécessaire de définir le type de paiement par pays. Sinon, il est impossible de clôturer la commande"; +$strings['CountryEmpty'] = "Pour passer commande, il est nécessaire de définir le champ Pays dans le profil utilisateur."; +$strings['Duration'] = "Durée"; +$strings['SubscriptionAdd'] = "Ajouter souscription"; +$strings['SubscriptionList'] = "Liste des souscriptions"; +$strings['SubscriptionListOnSale'] = "Souscriptions en promo"; +$strings['SelectSubscription'] = "Sélectionner la durée"; +$strings['SubscriptionNotValid'] = "Souscription non valable"; +$strings['SubscriptionSalesReport'] = "Rapport de ventes"; +$strings['BuySubscriptions'] = "Souscrire"; +$strings['ConfigurationOfSubscriptionsAndPrices'] = "Configuration des souscriptions et prix"; +$strings['FrequencyConfig'] = "Configuration périodes"; +$strings['Subscriptions'] = "Souscriptions"; +$strings['HasSubscriptions'] = "A des souscriptions"; +$strings['FrequencyRemoved'] = "Période supprimée"; +$strings['SubscriptionPeriodOnUse'] = "Période de souscription utilisée"; +$strings['FrequencyNotExits'] = "Cette période n'existe pas"; +$strings['FrequencyIncorrect'] = "Période incorrecte"; +$strings['SubscriptionFrequencyValueDays'] = "Valeur en jours"; +$strings['FrequencyNotUpdated'] = "Période non mise à jour"; +$strings['FrequencyNotSaved'] = "Période non sauvegardée"; +$strings['NeedToAddDuration'] = "Une durée est nécessaire"; +$strings['SubscriptionNotValid'] = "Souscription non valable"; +$strings['SelecSubscription'] = "Sélectionnez une souscription"; +$strings['ConfigureSubscriptionsFrequencies'] = "Configurez des périodes de souscription"; +$strings['FrequencyAdd'] = "Ajouter période"; +$strings['SubscriptionAlreadyExists'] = "La souscription existe déjà"; +$strings['SubscriptionPeriodDuration'] = "Durée de souscription (en jours)"; +$strings['Product'] = "Produit"; +$strings['SalePrice'] = "Prix"; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = "Votre abonnement a été activé, vous avez maintenant accès au module choisi."; +$strings['hide_shopping_cart_from_course_catalogue'] = "Cacher le chariot de vente dans le catalogue de cours et laisse le bouton s'inscrire"; diff --git a/plugin/buycourses/lang/spanish.php b/plugin/buycourses/lang/spanish.php new file mode 100644 index 000000000..cfc31902c --- /dev/null +++ b/plugin/buycourses/lang/spanish.php @@ -0,0 +1,321 @@ + %s , este porcentaje será descontado del total del precio del producto cuya diferencia será el monto base para aplicar estas comisiones."; +$strings['CoursesInSessionsDoesntDisplayHere'] = "Los cursos que se encuentren dentro de una sesión de formación no aparecerán en la lista de cursos a configurar como productos individuales."; +$strings['WantToSellCourses'] = "¿Quieres enseñar y ganar dinero con tus propios cursos?, Esta lista puede estar llena de tus ganancias vendiendo cursos a través de esta plataforma!. Informate con nosotros! "; +$strings['SelectOptionToProceed'] = "Selecciona una opción para proceder"; +$strings['VerifyTotalAmountToProceedPayout'] = "Por favor verificar el monto total para proceder con el pago de comisiones, este monto no considera cargos extras por paypal, las ventas de cursos que no cuenten con una cuenta de Paypal para el o los beneficiarios no serán consideradas."; +$strings['TotalAcounts'] = "Total de cuentas:"; +$strings['TotalPayout'] = "Total a pagar:"; +$strings['PayoutDate'] = "Fecha del Pago:"; +$strings['CautionThisProcessCantBeCanceled'] = "Precaución: este proceso puede tomar unos minutos y no puede ser cancelado."; +$strings['ProcessingPayoutsDontCloseThisWindow'] = "Procesando los pagos, no cierre esta ventana hasta que haya finalizado"; +$strings['PayoutSuccess'] = "Los pagos se han realizado con éxito"; +$strings['Buyer'] = "Comprador"; +$strings['BankTransfer'] = "Transferencia Bancaria"; +$strings['SaleInfo'] = "Información de la venta"; +$strings['SaleStatusPending'] = "Venta pendiente"; +$strings['SaleStatusCanceled'] = "Venta cancelada"; +$strings['SaleStatusCompleted'] = "Venta completada"; +$strings['PayoutStatusPending'] = "Pago pendiente"; +$strings['PayoutStatusCanceled'] = "Pago cancelado"; +$strings['PayoutStatusCompleted'] = "Pago completado"; +$strings['PayoutsTotalPending'] = "Pagos pendientes:"; +$strings['PayoutsTotalCanceled'] = "Pagos cancelados:"; +$strings['PayoutsTotalCompleted'] = "Pagos completados:"; +$strings['Total'] = "Total"; +$strings['TotalAmount'] = "Monto total:"; +$strings['CourseListOnSale'] = "Lista de cursos a la venta"; +$strings['AvailableCourses'] = "Cursos disponibles"; +$strings['Price'] = "Precio"; +$strings['SearchFilter'] = "Filtro de búsqueda"; +$strings['MinimumPrice'] = "Precio mínimo"; +$strings['MaximumPrice'] = "Precio máximo"; +$strings['AvailableCoursesConfiguration'] = "Configuración de cursos disponibles"; +$strings['PaymentsConfiguration'] = "Configuración de Pagos"; +$strings['TheUserIsAlreadyRegisteredInTheCourse'] = "Usted ya está registrado en el curso."; +$strings['SeeDescription'] = "Ver descripción"; +$strings['Buy'] = "Comprar"; +$strings['WaitingToReceiveThePayment'] = "Se encuentra a la espera de recibir el pago"; +$strings['TheUserIsAlreadyRegisteredInTheSession'] = "Usted ya está registrado en la sesión"; +$strings['ItemNotSaved'] = "Elemento no guardado"; +$strings['TitlePlugin'] = "Todo lo que necesita para enseñar y vender cursos en línea"; +$strings['PluginPresentation'] = "El Plugin BuyCourses le da los recursos para vender sus cursos o sus sesiones ya creados y enseñar en línea, todo en unos simples pasos y configuraciones. ¿Qué espera? Empiece a vender cursos a través de Chamilo ahora!"; +$strings['Instructions'] = "Instrucciones de uso"; +$strings['InstructionsStepOne'] = "Crea un curso o una sesión de formación en la plataforma."; +$strings['InstructionsStepTwo'] = "En la sección configuración de pagos, configure el tipo de moneda con el que venderá sus cursos o sesiones."; +$strings['InstructionsStepThree'] = "Configure los cursos a vender en la sección Configuración de cursos y precios"; +$strings['BuyCourses'] = "Comprar cursos"; +$strings['ConfigurationOfCoursesAndPrices'] = "Configuración de cursos y precios"; +$strings['SalesReport'] = "Reporte de ventas"; +$strings['UserInformation'] = "Ficha del comprador"; +$strings['PaymentMethods'] = "Métodos de pago"; +$strings['ConfirmOrder'] = "Confirmar Orden"; +$strings['PurchaseData'] = "Datos de la compra"; +$strings['bc_subject'] = "Confirmación pedido de cursos"; +$strings['PurchaseStatusX'] = "Estado de la compra: %s"; +$strings['PendingReasonByTransfer'] = "Pendiente. Esperando la confirmación de la transferencia"; +$strings['CancelOrder'] = "Cancelar Orden"; +$strings['BankAccountInformation'] = "Información de la Cuenta Bancaria"; +$strings['BankAccount'] = "Cuenta Bancaria"; +$strings['OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'] = "Una vez confirmado, recibira un e-mail con los datos bancarios y una referencia del pedido."; +$strings['SubscriptionToCourseXSuccessful'] = "

Proceso completado. Tu subscripción a \"%s\" se realizó correctamente.

"; +$strings['OrderCanceled'] = "Pedido cancelado"; +$strings['OrderStatus'] = "Estado del pedido"; +$strings['PayoutStatus'] = "Estado del pago"; +$strings['SearchByStatus'] = "Buscar por estado"; +$strings['OrderReference'] = "Referencia del pedido"; +$strings['OrderDate'] = "Fecha del pedido"; +$strings['OrderPrice'] = "Precio del pedido"; +$strings['ProductType'] = "Tipo de producto"; +$strings['SubscribeUser'] = "Inscribir usuario"; +$strings['DeleteOrder'] = "Eliminar pedido"; +$strings['ErrorContactPlatformAdmin'] = "Se ha producido un error desconocido. Por favor, póngase en contacto con el administrador de la plataforma."; +$strings['PendingReasonByAddress'] = "Pendiente. No has ingresado una dirección de correo confirmada."; +$strings['PendingReasonByAuthorization'] = "Pendiente. No hemos recibido los fondos."; +$strings['PendingReasonByEcheck'] = "Pendiente. El pago fue hecho a través de un eCheck que aún no está declarado"; +$strings['PendingReasonByIntl'] = "Pendiente. No tenemos un mecanismo de retiro de fondos."; +$strings['PendingReasonByMulticurrency'] = "Pending. No podemos realizar el cambio de moneda."; +$strings['PendingReasonByOrder'] = "Pendiente. Pedido realizado. No hemos recibido los fondos."; +$strings['PendingReasonByPaymentReview'] = "Pendiente. El pago está siendo revisado por PayPal por riesgo."; +$strings['PendingReasonByRegulatoryReview'] = "Pendiente. El pago está siendo revisado por cumplimiento con regulaciones gubernamentales."; +$strings['PendingReasonByUnilateral'] = "Pendiente. La dirección de correo electrónico aún no está registrada o confirmada."; +$strings['PendingReasonByUpgrade'] = "Pendiente. El pago fue realizado a través de una tarjeta de crédito."; +$strings['PendingReasonByVerify'] = "Pendiente. Lo sentimos. Todavía no estamos verificados en PayPal."; +$strings['PendingReasonByOther'] = "Pendiente. Por favor contacta al administrador de la plataforma."; +$strings['PayPalPaymentOKPleaseConfirm'] = "PayPal nos indicó que todo estaba listo para ejecutar el pago. Por seguridad, le pedimos confirme una última vez su pedido dando clic en el botón de confirmación a bajo. Una vez le haya dado clic, será registrado al curso y el monto correspondiente será retirado de su cuenta PayPal. Siempre puede acceder a sus cursos a partir de la pestaña 'Mis cursos'. Gracias por su compra!"; +$strings['Sandbox'] = "Entorno de Pruebas"; +$strings['PayPalAccount'] = "Cuenta de Paypal"; +$strings['NoPayPalAccountDetected'] = "No se ingresó una cuenta de Paypal"; +$strings['PayPalConfig'] = "Configuración PayPal:"; +$strings['TransfersConfig'] = "Configuración de transferencias:"; +$strings['PluginInstruction'] = "Puede activar o desactivar la opción de pagos vía PayPal, Culqi o configuración de transferencias bancarias, en la sección del configuración del plugin."; +$strings['ClickHere'] = "Clic aquí para más detalles"; +$strings['CurrencyType'] = "Tipo de moneda"; +$strings['InfoCurrency'] = "Permite configurar el tipo de moneda (de pago) para sus cursos."; +$strings['ApiUsername'] = "Nombre de usuario de API"; +$strings['ApiPassword'] = "Contraseña de API"; +$strings['ApiSignature'] = "Firma"; +$strings['InfoApiCredentials'] = "Para generar tu credenciales API para integrar su cuenta PayPal con Chamilo LMS, deberá seguir los siguientes pasos"; +$strings['InfoApiStepOne'] = "Ir a la opción de Perfil de PayPal, luego en Mis herramientas de venta"; +$strings['InfoApiStepTwo'] = "En el apartado Acceso API, dar clic en la opción Actualizar"; +$strings['InfoApiStepThree'] = "En la opción 2, de Configuración de credenciales y permisos de API, dar clic en Ver firma de API. Copiar los datos de las credenciales en el formulario de configuración de este plugin"; +$strings['ErrorOccurred'] = "Ocurrio un error. Codigo: %s. Mensaje: %s. Por favor, contacta al administrador de la plataforma."; +$strings['VisibleInCatalog'] = "Visible en el catálogo"; +$strings['Beneficiaries'] = "Beneficiarios"; +$strings['AvailableCourse'] = "Curso disponible"; +$strings['ShowOnCourseCatalog'] = "Mostrar en el catálogo de cursos"; +$strings['ByStatus'] = "Por estado"; +$strings['ByUser'] = "Por usuario"; +$strings['ByEmail'] = "Por email"; +$strings['PaymentMethod'] = "Método de pago"; +$strings['SWIFT'] = "Código SWIFT"; +$strings['SWIFT_help'] = "Formato estándar de los Códigos de Identificación Bancaria (BIC) que sirve como identificador único para un banco o institución financiera."; +$strings['PleaseSelectThePaymentMethodBeforeConfirmYourOrder'] = "Seleccione su método de pago preferido antes de confirmar su pedido"; +$strings['NoPaymentOptionAvailable'] = 'No hay opción de pago disponible. Por favor reporte este problema al administrador.'; +$strings['XIsOnlyPaymentMethodAvailable'] = '%s es la única opción de pago disponible para esta compra.'; +$strings['hide_free_text'] = "Esconder texto 'Gratis'"; +$strings['culqi_enable'] = "Activar culqi"; +$strings['include_services'] = "Incluir Servicios"; +$strings['CurrencyIsNotConfigured'] = "Configure una moneda antes de seguir."; +$strings['Services'] = "Servicios"; +$strings['Service'] = "Servicio"; +$strings['NewService'] = "Nuevo servicio"; +$strings['ServiceName'] = "Nombre de servicio"; +$strings['AppliesTo'] = "Aplicado a"; +$strings['ServiceInformation'] = "Información del servicio"; +$strings['ListOfServicesOnSale'] = "Lista de servicios en venta"; +$strings['GlobalConfig'] = "Configuración global"; +$strings['WriteHereTheTermsAndConditionsOfYourECommerce'] = "Escriba aquí los términos y condiciones de su portal e-commerce"; +$strings['EditService'] = "Editar servicio"; +$strings['DeleteThisService'] = "Borrar servicio"; +$strings['IConfirmIReadAndAcceptTermsAndCondition'] = "He leído y acepto los términos y condiciones"; +$strings['PleaseSelectTheCorrectInfoToApplyTheService'] = "Porfavor Seleccione la información correcta para aplicar el servicio"; +$strings['SaleStatusCancelled'] = "Venta anulada"; +$strings['ServiceSaleInfo'] = "Información del servicio"; +$strings['ServiceId'] = "Id de servicio"; +$strings['BoughtBy'] = "Comprado por"; +$strings['PurchaserUser'] = "Usuario comprador"; +$strings['Pending'] = "Pendiente"; +$strings['Names'] = "Nombres"; +$strings['SellerName'] = "Nombre vendedor"; +$strings['SellerId'] = "Identificador vendedor"; +$strings['SellerAddress'] = "Dirección vendedor"; +$strings['SellerEmail'] = "E-mail vendedor"; +$strings['NextNumberInvoice'] = "Número siguiente factura"; +$strings['NextNumberInvoiceDescription'] = "Número de la siguiente factura asignado de forma manual"; +$strings['InvoiceSeries'] = "Serie factura"; +$strings['InvoiceSeriesDescription'] = "Parámetro opcional: Ejemplo de numeración factura <serie><año>/<número>"; +$strings['InvoiceView'] = "Ver factura"; +$strings['NoInvoiceEnable'] = "No está habilitado el bloque de facturación"; +$strings['Company'] = "Empresa"; +$strings['VAT'] = "CIF"; +$strings['Address'] = "Dirección"; +$strings['InvoiceNumber'] = "Num. factura"; +$strings['InvoiceDate'] = "Fecha de emisión"; +$strings['Invoice'] = "Factura"; +$strings['SaleEmail'] = "E-mail de ventas"; +$strings['PurchaseDetailsIntro'] = "Detalles de la comprar"; +$strings['PurchaseDetailsEnd'] = "Atentamente"; +$strings['ProductName'] = "Nombre producto"; +$strings['BankAccountIntro'] = "Información cuentas bancarias"; +$strings['AdditionalInfoRequired'] = 'Se requiere que se elija la información adicional antes de proceder'; +$strings['SubscriptionToServiceXSuccessful'] = "La subscripción al servicio %s ha sido satisfactoria"; +$strings['ClickHereToFinish'] = "De clic aquí para terminar"; +$strings['OrderCancelled'] = "Pedido anulado"; +$strings['use_currency_symbol'] = "Usar símbolo de la moneda"; +$strings['ExportReport'] = "Exportar reporte de ventas"; +$strings['OrderTime'] = "Fecha del pedido"; +$strings['SelectDateRange'] = "Seleccione una fecha de inicio y una fecha de fin para el reporte"; +$strings['ServiceAdded'] = "Servicio agregado"; +$strings['ServiceEdited'] = "Servicio editado"; +$strings['ListOfServicesOnSale'] = "Lista de servicios a la venta"; +$strings['AdditionalInfo'] = "Información adicional"; +$strings['culqi_enable'] = "Habilitar Culqi"; +$strings['CulqiConfig'] = "Configuración de CULQI:"; +$strings['InfoCulqiCredentials'] = "Para obtener las credenciales es necesario crearse una cuenta en Culqi e ingresar en modo desarrollo, copiar el código de comercio que se encuentra en su panel de control, luego ingresar al apartado de API Keys y generar la Key correspondiente para copiarla aquí"; +$strings['CommerceCode'] = "Codigo de comercio"; +$strings['NoTermsAndConditionsProvided'] = "Terminos y condiciones no establecidos"; +$strings['GlobalConfig'] = "Configuración global:"; +$strings['MyServices'] = "Mis servicios"; +$strings['SalePrice'] = "Precio de venta"; +$strings['YouNeedToBeRegisteredInAtLeastOneCourse'] = "Necesitas estar registrado en al menos un curso"; +$strings['YouNeedToBeRegisteredInAtLeastOneSession'] = "Necesitas estar registrado en al menos una sesión"; +$strings['IfYouWantToGetTheCertificateAndOrSkillsAsociatedToThisCourseYouNeedToBuyTheCertificateServiceYouCanGoToServiceCatalogClickingHere'] = "Si quieres obtener el certificado y/o las competencias asociadas a este curso, necesitas comprar el servicio de Certificado , puedes ir al catálogo de servicios para comprarlo haciendo click AQUÍ"; +$strings['ServiceDeleted'] = 'Servicio eliminado'; +$strings['YourCoursesNeedAtLeastOneLearningPath'] = 'Los cursos en los que estás registrado necesitan tener al menos una lección que contenga un item de cerficado final'; +$strings['GlobalTaxPerc'] = "Porcentaje del impuesto global"; +$strings['GlobalTaxPercDescription'] = "Porcentaje por defecto que se usará, excepto si existe un impuesto específico en el curso, sesión o servicio."; +$strings['TaxPerc'] = "Porcentaje del impuesto"; +$strings['TaxPercDescription'] = "Si se deja vacío se usará valor global por defecto."; +$strings['ByDefault'] = "por defecto (valor global)"; +$strings['OnlyCourses'] = "Solo Cursos"; +$strings['OnlySessions'] = "Solo Sesiones"; +$strings['OnlyServices'] = "Solo Servicios"; +$strings['TaxAppliesTo'] = "Impuestos aplicados a"; +$strings['AllCoursesSessionsAndServices'] = "Todos (Cursos, sesiones y servicios)"; +$strings['TaxNameCustom'] = "Nombre del impuesto"; +$strings['TaxNameExamples'] = "VAT, IVA, IGV, TVA, IV ..."; +$strings['ErrorUpdateFieldDB'] = "Error al actualizar los campos de la base de datos"; +$strings['tpv_redsys_enable'] = "Habilitar TPV RedSys"; +$strings['tpv_redsys_enable_help'] = "Para poder utilizar la modalidad de pago del TPV de RedSys es necesario descargar los ficheros de \"INTEGRACIÓN REST - API PHP\" en el siguiente enlace web de RedSys y ubicar el fichero el fichero apiRedSys.php en el directorio plugin/buycourses/resources."; +$strings['NotFindRedsysFile'] = "No se encuentra en el directorio plugin/buycourses/resources el fichero apiRedsys.php"; +$strings['TpvPayment'] = "Pago con tarjeta"; +$strings['TpvRedsysConfig'] = "Configuración TPV Redsys"; +$strings['DS_MERCHANT_MERCHANTCODE'] = "Número de comercio (FUC)"; +$strings['DS_MERCHANT_TERMINAL'] = "Número de terminal"; +$strings['DS_MERCHANT_CURRENCY'] = "Moneda del terminal"; +$strings['kc'] = "Clave secreta de encriptación"; +$strings['url_redsys'] = "URL conexión Redsys"; +$strings['url_redsys_sandbox'] = "URL conexión Redsys (Pruebas)"; +$strings['InfoTpvRedsysApiCredentials'] = "Deberá completar los siguientes campos del formulario con la información que les facilite el Soporte Técnico del TPV Redsys:"; +$strings['InfoEmailExtra'] = "Información extra en e-mail"; +$strings['Coupon'] = "Cupón"; +$strings['DiscountAmount'] = "Descuento total"; +$strings['RedeemCoupon'] = "Canjear"; +$strings['NeedToAddCouponCode'] = "No se ha introducido un código de cupón"; +$strings['CouponNotValid'] = "El cupón no es valido"; +$strings['CouponRedeemed'] = "Cupón canjeado"; +$strings['CouponDiscount'] = "Descuento"; +$strings['CouponStatus'] = "Estado cupón"; +$strings['CouponPercentage'] = "Porcentaje"; +$strings['CouponAmount'] = "Valor"; +$strings['CouponList'] = "Lista de cupones"; +$strings['CouponCode'] = "Código del cupón"; +$strings['CouponDiscountType'] = "Tipo de descuento"; +$strings['CouponDateStart'] = "Valido desde"; +$strings['CouponDateEnd'] = "Caduca el"; +$strings['CouponDelivered'] = "Canjeados"; +$strings['CouponDisable'] = "Desactivar"; +$strings['CouponEnable'] = "Activar"; +$strings['CouponCodeUsed'] = "Código de cupón ya usado"; +$strings['CouponNoExists'] = "El cupón no existe"; +$strings['CouponErrorInsert'] = "Error añadiendo nuevo cupón"; +$strings['CouponActive'] = "Activo"; +$strings['CouponDisabled'] = "Desactivado"; +$strings['CouponUpdate'] = "Cupón actualizado"; +$strings['CouponsConfiguration'] = "Configuración de cupones"; +$strings['CouponAdd'] = "Nuevo cupón"; +$strings['ConfigureCoupon'] = "Configurar cupón"; +$strings['DiscountCoupons'] = "Cupones descuento"; +$strings['CouponsCode'] = "Código"; +$strings['DoYouHaveACoupon'] = "¿Tienes un cupón?"; +$strings['stripe_enable'] = "Activar Stripe"; +$strings['StripeConfig'] = "Configuración de Stripe:"; +$strings['InfoStripeCredentials'] = "Para obtener las credenciales debes crear una cuenta en Stripe, copiar el id de cuenta de tu perfil, ir a la sección de configuración de las claves API y copiar la secret key, por último deberá registrar un nuevo Endpoint en la sección de webhooks a https://{site}/plugin/buycourses/src/stripe_response.php para el tipo de evento checkout.session.completed y copiar el secret del Endpoint."; +$strings['StripeAccountId'] = "Id de la cuenta:"; +$strings['StripeSecret'] = "Secret key:"; +$strings['StripeEndpointSecret'] = "Endpoint secret:"; +$strings['PendingReasonByStripe'] = "Pendiente. Esperando confirmación del pago ..."; +$strings['cecabank_enable'] = "Habilitar TPV Cecabank"; +$strings['TpvCecabank'] = "Pago con tarjeta (Cecabank)"; +$strings['CecaSecret'] = "Secret"; +$strings['CecaUrl'] = "Url"; +$strings['CecaMerchanId'] = "Merchant"; +$strings['CecaAcquirerId'] = "Adquirer"; +$strings['CecaTerminalId'] = "Terminal"; +$strings['CecaCypher'] = "Cypher"; +$strings['CecaCurrency'] = "Moneda"; +$strings['CecaExponent'] = "Exponent"; +$strings['CecaSupportedPayment'] = "Pago soportado"; +$strings['CecabankConfig'] = "Configuración Cecabank"; +$strings['Country'] = "País"; +$strings['PaymentType'] = "Tipo de pago"; +$strings['CountryRelPaymentConfig'] = "Configuración del tipo de pago por país"; +$strings['CountryRelPaymentMessage'] = "Para realizar un pedido es necesario definir el tipo de pago por país, en caso contrario no se permite la realización del pedido"; +$strings['CountryEmpty'] = "Para realizar un pedido es necesario definir en el perfil del usuario el campo país"; +$strings['Duration'] = "Duración"; +$strings['SubscriptionAdd'] = "Añadir suscripción"; +$strings['SubscriptionList'] = "Lista de suscripciones"; +$strings['SubscriptionListOnSale'] = "Lista de suscripciones a la venta"; +$strings['SelectSubscription'] = "Seleciona duración"; +$strings['SubscriptionNotValid'] = "Suscripción no valida"; +$strings['SubscriptionSalesReport'] = "Reporte de ventas"; +$strings['BuySubscriptions'] = "Compra suscripciones"; +$strings['ConfigurationOfSubscriptionsAndPrices'] = "Configuración de suscripciones y precios"; +$strings['FrequencyConfig'] = "Configuración periodos"; +$strings['Subscriptions'] = "Suscripciones"; +$strings['HasSubscriptions'] = "Tiene suscripciones"; +$strings['FrequencyRemoved'] = "Periodo eliminado"; +$strings['SubscriptionPeriodOnUse'] = "Periodo de suscription en uso"; +$strings['FrequencyNotExits'] = "El periodo no existe"; +$strings['FrequencyIncorrect'] = "Periodo incorrecto"; +$strings['SubscriptionFrequencyValueDays'] = "Valor de la suscripción en días"; +$strings['FrequencyNotUpdated'] = "Periodo no actualizado"; +$strings['FrequencyNotSaved'] = "Periodo no guardado"; +$strings['NeedToAddDuration'] = "Hay que especificar una duración"; +$strings['SubscriptionNotValid'] = "Suscripción no valida"; +$strings['SelecSubscription'] = "Seleccione una suscripción"; +$strings['ConfigureSubscriptionsFrequencies'] = "Configurar los períodos de suscripciones"; +$strings['FrequencyAdd'] = "Agregar período"; +$strings['SubscriptionAlreadyExists'] = "La suscripción ya existe"; +$strings['SubscriptionPeriodDuration'] = "Duración de suscripción (en días)"; +$strings['Product'] = "Producto"; +$strings['YouProductIsActivatedYouCanNowAccessIt'] = "Tu producto esta activado y puedes ahora acceder a este."; +$strings['hide_shopping_cart_from_course_catalogue'] = "Esconder el carito de compra del catalogo de curso y dejar el boton de subscripcion a curso"; diff --git a/plugin/buycourses/plugin.php b/plugin/buycourses/plugin.php new file mode 100644 index 000000000..f77cd31ed --- /dev/null +++ b/plugin/buycourses/plugin.php @@ -0,0 +1,13 @@ +Plugins). + * + * @package chamilo.plugin.buycourses + */ +/** + * Plugin details (must be present). + */ +require_once __DIR__.'/config.php'; +$plugin_info = BuyCoursesPlugin::create()->get_info(); diff --git a/plugin/buycourses/resources/css/style.css b/plugin/buycourses/resources/css/style.css new file mode 100644 index 000000000..40b07a1c5 --- /dev/null +++ b/plugin/buycourses/resources/css/style.css @@ -0,0 +1,421 @@ +.buy-courses-tabs { + margin-bottom: 15px; +} + +.buy-courses-page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} + +.buy-courses-block-button { + display: block; + width: 100%; + height: 50px; + line-height: 35px; + font-size: 16px; +} + +.buy-courses-cross-out { + text-decoration: line-through; +} + +.buy-courses-title-color { + color: #5DC3E1; + padding-bottom: 10px; +} + +.buy-courses-description-service { + text-align: justify; + font-size: 18px; + line-height: 1.42857; +} + +/* PROCESS BUY */ +.buy-info .price, .service-buy .price { + font-size: 24px; + line-height: 32px; + font-weight: bold; + padding-bottom: 10px; + padding-top: 10px; + letter-spacing: -0.020em; +} + +.buy-info .price-details-tax, .service-buy .price-details-tax { + font-size: 18px; + line-height: 24px; + font-weight: bold; + padding-bottom: 0px; + padding-top: 10px; + letter-spacing: -0.020em; +} + +.buy-info .buy-item .title { + margin-top: 5px; + font-weight: bold; + line-height: 24px; + font-size: 18px; +} + +.buy-info .buy-item .description, .buy-info .buy-item .coaches { + margin-bottom: 20px; + color: #666666; +} + +.buy-info .buy-item .list-description { + color: #666666; +} + +.buy-info .buy-item .date { + margin-bottom: 10px; + color: #666666; +} + +.buy-info .coupon-question, .service-buy .coupon { + font-size: 16px; + line-height: 6px; + font-weight: bold; + padding-bottom: 10px; + padding-top: 24px; + letter-spacing: -0.020em; +} + +.buy-info .coupon, .service-buy .coupon { + line-height: 32px; + font-weight: bold; + padding-bottom: 10px; + padding-top: 10px; + letter-spacing: -0.1em; +} + +.buy-summary { + margin-top: 10px; + border-top: 1px solid #CCCCCC; +} + +.panel-box-buy { + box-shadow: 0 15px 30px rgba(0, 0, 0, .1); + padding: 20px; +} + +.buy-summary .alert-info { + color: #31708f; + background-color: #e8f2f8; + border-color: #bce8f1; +} + +.btn.btn-default.btn-sm { + white-space: unset; +} + +tr.sale-row { + font-size: 12px; +} +tr.sale-columns { + font-size: 10px; + text-transform: uppercase; + background-color: #e6e6e6; +} +tr.sale-columns th{ + vertical-align: middle !important; + text-align: center; +} +@-moz-keyframes wobblebar-loader { + 0% { + left: 4px; + } + 3% { + left: 204px; + } + 6% { + left: 4px; + } + 9% { + left: 204px; + } + 12% { + left: 4px; + } + 15% { + left: 204px; + } + 18% { + left: 32px; + } + 27% { + left: 32px; + } + 30% { + left: 204px; + } + 33% { + left: 4px; + } + 36% { + left: 204px; + } + 39% { + left: 4px; + } + 42% { + left: 204px; + } + 45% { + left: 4px; + } + 48% { + left: 204px; + } + 51% { + left: 152px; + } + 63% { + left: 152px; + } + 66% { + left: 4px; + } + 69% { + left: 204px; + } + 72% { + left: 4px; + } + 75% { + left: 204px; + } + 78% { + left: 4px; + } + 81% { + left: 204px; + } + 84% { + left: 72px; + } + 94% { + left: 72px; + } + 97% { + left: 204px; + } +} + +@-webkit-keyframes wobblebar-loader { + 0% { + left: 4px; + } + 3% { + left: 204px; + } + 6% { + left: 4px; + } + 9% { + left: 204px; + } + 12% { + left: 4px; + } + 15% { + left: 204px; + } + 18% { + left: 32px; + } + 27% { + left: 32px; + } + 30% { + left: 204px; + } + 33% { + left: 4px; + } + 36% { + left: 204px; + } + 39% { + left: 4px; + } + 42% { + left: 204px; + } + 45% { + left: 4px; + } + 48% { + left: 204px; + } + 51% { + left: 152px; + } + 63% { + left: 152px; + } + 66% { + left: 4px; + } + 69% { + left: 204px; + } + 72% { + left: 4px; + } + 75% { + left: 204px; + } + 78% { + left: 4px; + } + 81% { + left: 204px; + } + 84% { + left: 72px; + } + 94% { + left: 72px; + } + 97% { + left: 204px; + } +} + +@keyframes wobblebar-loader { + 0% { + left: 4px; + } + 3% { + left: 204px; + } + 6% { + left: 4px; + } + 9% { + left: 204px; + } + 12% { + left: 4px; + } + 15% { + left: 204px; + } + 18% { + left: 32px; + } + 27% { + left: 32px; + } + 30% { + left: 204px; + } + 33% { + left: 4px; + } + 36% { + left: 204px; + } + 39% { + left: 4px; + } + 42% { + left: 204px; + } + 45% { + left: 4px; + } + 48% { + left: 204px; + } + 51% { + left: 152px; + } + 63% { + left: 152px; + } + 66% { + left: 4px; + } + 69% { + left: 204px; + } + 72% { + left: 4px; + } + 75% { + left: 204px; + } + 78% { + left: 4px; + } + 81% { + left: 204px; + } + 84% { + left: 72px; + } + 94% { + left: 72px; + } + 97% { + left: 204px; + } +} + +/* Landscape phones and down */ +@media (max-width: 480px) { + .panel-box-buy { + padding: 0; + } + + .buy-summary .pull-right { + float: left !important; + } + + .buy-summary .btn-success { + width: 100%; + } +} + +/* :not(:required) hides this rule from IE9 and below */ +.wobblebar-loader:not(:required) { + background: #2E6DA4; + -moz-border-radius: 10.66667px; + -webkit-border-radius: 10.66667px; + border-radius: 10.66667px; + display: inline-block; + overflow: hidden; + text-indent: -9999px; + width: 228px; + height: 21.33333px; + position: relative; +} + +.wobblebar-loader:not(:required)::after { + -moz-animation: wobblebar-loader 15000ms infinite ease; + -webkit-animation: wobblebar-loader 15000ms infinite ease; + animation: wobblebar-loader 15000ms infinite ease; + background: white; + display: block; + -moz-border-radius: 7.11111px; + -webkit-border-radius: 7.11111px; + border-radius: 7.11111px; + content: ''; + position: absolute; + top: 3.55556px; + left: 4px; + width: 21.33333px; + height: 14.22222px; +} + +div.items-course-info h4.title { + font-weight: bold; + height: 2.8em; +} + +@media (min-width: 991px) { + .col-sm-6 .items-course .items-course-image figure { + max-height: 135px; + } +} \ No newline at end of file diff --git a/plugin/buycourses/resources/img/128/backlogs.png b/plugin/buycourses/resources/img/128/backlogs.png new file mode 100644 index 0000000000000000000000000000000000000000..522a1cfa0d7414d6491d5a4dbe4dd3dae1016875 GIT binary patch literal 7904 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/128/buycourses.png b/plugin/buycourses/resources/img/128/buycourses.png new file mode 100644 index 0000000000000000000000000000000000000000..cba3a6492393cb74e8fe8a67613479b12a70da85 GIT binary patch literal 10381 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 AVgLXD literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/128/buysubscriptions.png b/plugin/buycourses/resources/img/128/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..97a4eed5298b7f7fc4e0167a70ea0c17aa592665 GIT binary patch literal 3816 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¨WnCzJGH@B7Ht3m8bhdpr%k~ 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/128/subscriptionssettings.png b/plugin/buycourses/resources/img/128/subscriptionssettings.png new file mode 100644 index 0000000000000000000000000000000000000000..9c50507130adf5560f4895422137077faeb406a7 GIT binary patch literal 6629 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/32/backlogs.png b/plugin/buycourses/resources/img/32/backlogs.png new file mode 100644 index 0000000000000000000000000000000000000000..7054fc702e4cc763c21e086e6f25e6d4254b2cca GIT binary patch literal 1338 zcmV-A1;zS_P)JOxr2a0W+EJxvz(!oczF^Unj=b58&ONSFe` zGz&GffB^v*0;0(DO(DWQbm)oc>)4(Uz#h#) zdVIs(xLBNl1t1s&(n)W^3^425vtk{hb!(878d<53acwfszVL{3y=^CejS~Z;9k@b{ zhc8IR?R$m>1DbHfjskr{Fogj@29#!av0bQfu5Rf6jy|he#YZOu7%K~R-JfnzGyo_d zlNo@mt^ok(7QM88YiO~z{>%OYw7P07pDqfZ)m<&N=(L!k0ZC}gY{cbWKjzksA`;T$ z3PAuC8mP=Te(hWH+1MJnKM%l3XQ3$s0>D~Ax&`Ouwh}1;pa^GsOK{|L98v2cn(EU9 zw4Q_&iSkYW`2d+w2ek5CNLYy2S223%t%7xDBq7uAG++-wYlTp093#Vhh}c&!OP>T3 z;h{N$c(`d000CJ8(0MLE5+IMq;@*-02chd1VTvH!O#LA z^CbJGM9h&TvrEng+CDA#7PSV=pEKN(3l5BCc=(+Ibn5dTs63fA{PXLzw__9Q2WN`8 zQZuyDV|k&Q#b?^I*vpY{aFM!+PoPSYgmC0;_+DCsI08aIgx}^T3_0Gm}bmQ z)R`?SR-paJ5xlZx3nHdjyY&n19~dT>0X9PxX5^iLp`n5OGwTyCR##Uy5eR3_oWZi? z%klB<-C$-suyiSWhJkP>ghX8;w@sYeEXQ_oP86WD;R-UQE@^4O-hKOE+cwUgI|oY$ zoV>)@L>#;{A-Z;crGCi+NG6kmMAY2S(D0Z6qtbER?LT#PCR$rt(cRsR(r6T}Qs}*M zb&vG22MZ3U)Lj>cJWi%5n{>;jc7A#Uf^H}X> zE*Jm^rvJ52AdrcdmveJtBbSH6EPU2)0K&y=^6Z7R-tQ0mJv9>H`l>1}4To9y0>2is zrHSqZMC60dw}F_}0vu&FdZ4mzF!MlBgM3>9rnwAE&j4HwGEPIf?=y3%K=)1;6Cxr2 wf|)1y9l9e-(KdhA0ROk#_rKyvfJuOV0GWPN;;10<@c;k-07*qoM6N<$fO+pgKB&qABS;Gdbpymff zl_*=cX%!&at$e6Lg7OmyX%!VhiiOl4fDpe53EPVFSuO$Uf0y?)O<=e*~6&U4>;&N0zilA$*-U5f9R2McZI`FjXh=x^%FwKV}oCq+}+!i|Mt+(PJQCQ znH144V`yXqPSgUWLHf`8_8f?IIK+%6%!`>}>)Abrez~i=)7aIWHu&skOW%9+I$#Y0 zB#uaYAh{)hg9i_qESuZg(qs1@nw*>@Kb*C??0DxBon5`?Pa2qco5EuZQNw`dJenRF znd;4C5Y1%KOK|DOgd}alL?RM_Z?8d$*_YZd<}P63@8)>ZOfAy-pH+9Vxi1gh+o9k-j9%ZU+%AtT|@{U1?#4l*%(m zrBa9*5%``*b89Ph)V6_$1(z9XFX?zPZkmWk4DhvE?BN}=mb6D9gGCfq=-f^By?+%_ zs|%NJ7M7noeuZrH#pMou>_EShnX-%sdh-w9#AiMSAptJ+CeI}xqy`fj0h6Fi)jSvQ zsa&s1sUW0;loGB_@y|D>_m<8*^{TN69l!_PW&MRpCIP^AD^MCx8qjs$VoHN^11fxz`z&>WqjRN)+(ZO?y1*+rhTBfjE|3NXlMZ7)UShSNL2%D5D`Lw zaRJH&XdS%aJ<8z*42*M7&YQeI5O_BmiMp+=SSvuOy9#ijK)C{C5&>86sX)aOaD4%n z*2r+7Af>F!eph}W6?~uluCd#O6Cia*YO7Zo5K4h@g|a76u5y$;fi+LSqhfvXFwjOj z=K@ui|GWVcN`N%grP5$RVR2R9#zKIqFSndL479o33j#HgS$U-~_m(+O7ryqZH5ivD zt#VwQaZ#?amV~-rFL`k1*GZn?nRlfatmYi($)$u`Uw92{(k&*$@htM{$rgua48p+J_FmL_)Y z-1(2ghYt@83=CXbTwL5&C=?>7WpZ096bdMnN-gmV3k&Zr6bjL1oVNSG;NW0!dV2aj zXV0Gfex*{`lgs7S0El)mA@M&bm&;@Me13C$*ed`)jvP61a`*1t)HF>!Iy%aa9zFWZ zMhWjH{`m3Zr#Hs8O+rElId$q(hhZ3p2q9Bv&YW4shK;0MX^u~&Qd6f-pWYPTmO%4F lq5l`RNhB<=wKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/32/buysubscriptions.png b/plugin/buycourses/resources/img/32/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..359169ef617a13a8bae29a3f6340a407c6d2caf7 GIT binary patch literal 1263 zcmVwKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/32/discount.png b/plugin/buycourses/resources/img/32/discount.png new file mode 100644 index 0000000000000000000000000000000000000000..ec27b0093be29408d5eff2073e658c72be3e1c63 GIT binary patch literal 4181 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/32/settings.png b/plugin/buycourses/resources/img/32/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..00ded5c2729c9165e0930f03d374592900cf2878 GIT binary patch literal 1695 zcmV;Q24MM#P)ThZwM4*=?a z5#;dU!nkzR$7>0qa>-hbCqS0v3?RHlm zJ9g}5dI_=|&L54(O-JL~y-dUfB1s2;z(QD7+9$BUO8IZ|W!voRSih0s$q9bEtDbK+ zzsa7Ojf4i;X=-Z1?RL}9(12kW*+oS~-vth&0i+b3pLO1#SNDK4!8EZ;Ntwq(pWhFr z36=#`vM=2>O)xE7O7ZYRdF=nxr_(C@v|%p*gS~y6J$n|H%S9{}Ln(#Z?e2lQk}wdz zb5A(4cQ19NrF`?$DSm$beA>Ucv=pDmlWe3wo-Hq@&gUZ$Ptf2fkV z8{qwp3)HNyWboQGq?A-uRdss3-nRPs`aeGqVB7KIe72&3Uw3ugZx|lCon2*Rgu`K! zQaD}Cq~DXvTp&a^9Omo$_Tde881G*3e6hd3e|T|j#*L`1w3Pk>2Y9Q#o{KNNgiXqf z0CTYzonvDJgF$9zW{JgO0E9yGn3h2>7^L~b4x}c~H0@SXQ-}d%090<^P;|5cqxh3aiRNxi&UpYGY z4grUaeVZKw<2ui`@8o(Y3xExk)!3ARR4}P&oPFUZymPIYUtHY5`H>O&$H)2ArAtW5 zA}2!%WmMo$xP#w#YsjB}nTkJtjf)=@@nS{#456m1oDEgg=&9t?r9`Aa_7wE-ea{=@ z+ALf)8xET-qjF0EY_N^<&~%iy3mQ4SHOw2IjB;*z3t#s3qDzTgDKsGv2-G5gN{T6k zMx|?(y6h3M?Fy%^C-;mDw&W80JyyuT(@*hcMK2$IaD(DJ2VdBn$6$LAN=hUkga83@ z5kO16izWmz^=y+8Q`5mTv5b$UfB;~xeu*rn0wYc^80Y%+5Q+r(HEYS!6DTPWiwdNI zErMv8K#CNAQrJ?8eM|+Cr`xO5w+KcA4(kzCJ(^3|+C0LhiCv!}h7F}@_$Mc@>pIHg zp|z%lDUXLUBee|86fzrk^X_~tLg?sHVrBS*?*WiW0T{hC%M*{UVrsOVM@pSI77P|* z8g>)5tAFq(V-vjt|3U(?q=Z*|J{0hS3%gN(5`y<4n^2l~V9Ay4H%-`DoW;fNtE^tL zhMBN|+X0$cKzPBALs!hjiYf8V6B-OAYk*X}prsBZ4It&ey?(GvlO5$bm`fDkX@HMH zX4ms&M#*;H0xtP5%8W9*P>wXhn7Qj%h>w%SHJ-~aVr!-E0q=FEIh5pyRoPs<8J?J# znMo`O;7|Cz;j?#r{`84Hq3ar7YN){#^kb@gG{RVblukT3!VE{UTmKV)81R>X8JRcp zf4P7E3-KiZ*13)sx^akSo_>_S^-K{;fH^&kT%AokB(Z0&VQzkgEPZTr%d=nI2`tMW zP5LvOna-P;y){OGN2Bf;9~d2ps45{Ajp4`*V$JvCOk7>)4t(W1%M~2%D$CnnulDEV zMZB8ZHZ&|>{9%^Sy(zyaQfmv(JA(^F{S%2zzu&g&TYvbk0h~N}GT$(ajhdzn?cTk6 pK~z|U&6itjTt^wle{=Ti&E9qFU3=}dBgctzHz*=OEwofL zRcVD#=mSt96$uH^0#7`E;E~WGR0tR$@j{Cbaat50K}0kp!ic5{O;yp9IIxZrCr-1D zZ)@+a_rB*cXLva4wKuU{0%^tn>6|m)I6k$G>!6&-wN20kri771|SCzc*l8R(o&r^nLFZEND3Ue16|1 z$8mHV`IUFG2M_@2z&AIeiJzhvSUGs{(~m|HANpWxAy?wS$ul!OH1ys)`FbZ@F#LOrUgJWk;0x^vBCnhpaP5Dm3~X}Y1D!M-xi0hlOxD8ipbnN;dpt? z3tX zzYBn(r82w^6}VP{s-*mpsAyjLw(dz-EE|kY%#bU%=Z};QV>f1*o?oonyfd9<{AQ~D zE>amhm&OrQ6M#z9WOQPN^kQk%9P1Joeq)ruBg6dh><9qk$rL~O)t~vS8ls~uOuQE3cKm;9;Qc@Yfx)3+o<20h=)?>F z-jlD<{Mm2ed-*ir3htTj9_4#KKUMen`t!fxYtOw*q2zAw7mlCf=|e-jec3$`|Mrh- zeEB;=y!6VOs}g9?fMMDw1(qcN5QeK{YKpcjiQ^~?bGb{JmW^dgZ0UBweciJ>*?)~r z@jCjg79N#zeDo83lA?s<{yPXXXrNLxDOF6Ots%E%*b^8%1wED`YQGza|v z6iQWW=@1E)@!$L*o>HO~SwM6E7XIb-fP%ng1EV{hukPvT4X#RH)f)W=5XLM`sl!mX z304Iv0t}!MfC><_q00u@3~il?^z?4=z=<)qO#^@U>qQ2KhM7ub0eJgzlEEXxoWFP- zfcb2JpB()Q$4>u!g_k0bJPgGt2=+i)gS-GkfNn#R33)FhLJ;bN)SRGboLlm4An?!e zDMqi)Fgu?I;GL-jMki*to}33@AzLJIZJLoQx9UFn;_GPTF)#{n;|46rBN&98+achC z)+%Tc-b#U|1QzY;)lYo`Y9h29e7XjofBeJDW%BIW5(nU8d$$p73$d*?2Edk$9enYL z2Z*#ZtswBK`%n>J*BZXp3w?c%&BB(=aCID74VVc*#tXUyws;2ku?~>2&|`ks-q*?Y zzRr5^SR_m=60X1deL4^A-P*AJTKTH`V614OUN2ZC%+7&rgQkH;g4cnZ7rHF4-TRUb zFj*m2l*ED_?iyP+#6iR%dlitiYLvk+Vb@N`WWjRpiFh2aHR#_BwhYt>I53aT?)ehN4Qm(xD2K;>I6`HRSB+YFrdWUT}T|^?eADAB4*A7k-cv%#W zoOrxpNwrS^0000T+ literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/32/subscriptionssettings.png b/plugin/buycourses/resources/img/32/subscriptionssettings.png new file mode 100644 index 0000000000000000000000000000000000000000..21f37ff019db9ff36684650041f37cb3beaa61a1 GIT binary patch literal 1680 zcmV;B25K~z|U&6itjTt^wle{=Ti&E9qFU3=}dBgctzHz*=OEwofL zRcVD#=mSt96$uH^0#7`E;E~WGR0tR$@j{Cbaat50K}0kp!ic5{O;yp9IIxZrCr-1D zZ)@+a_rB*cXLva4wKuU{0%^tn>6|m)I6k$G>!6&-wN20kri771|SCzc*l8R(o&r^nLFZEND3Ue16|1 z$8mHV`IUFG2M_@2z&AIeiJzhvSUGs{(~m|HANpWxAy?wS$ul!OH1ys)`FbZ@F#LOrUgJWk;0x^vBCnhpaP5Dm3~X}Y1D!M-xi0hlOxD8ipbnN;dpt? z3tX zzYBn(r82w^6}VP{s-*mpsAyjLw(dz-EE|kY%#bU%=Z};QV>f1*o?oonyfd9<{AQ~D zE>amhm&OrQ6M#z9WOQPN^kQk%9P1Joeq)ruBg6dh><9qk$rL~O)t~vS8ls~uOuQE3cKm;9;Qc@Yfx)3+o<20h=)?>F z-jlD<{Mm2ed-*ir3htTj9_4#KKUMen`t!fxYtOw*q2zAw7mlCf=|e-jec3$`|Mrh- zeEB;=y!6VOs}g9?fMMDw1(qcN5QeK{YKpcjiQ^~?bGb{JmW^dgZ0UBweciJ>*?)~r z@jCjg79N#zeDo83lA?s<{yPXXXrNLxDOF6Ots%E%*b^8%1wED`YQGza|v z6iQWW=@1E)@!$L*o>HO~SwM6E7XIb-fP%ng1EV{hukPvT4X#RH)f)W=5XLM`sl!mX z304Iv0t}!MfC><_q00u@3~il?^z?4=z=<)qO#^@U>qQ2KhM7ub0eJgzlEEXxoWFP- zfcb2JpB()Q$4>u!g_k0bJPgGt2=+i)gS-GkfNn#R33)FhLJ;bN)SRGboLlm4An?!e zDMqi)Fgu?I;GL-jMki*to}33@AzLJIZJLoQx9UFn;_GPTF)#{n;|46rBN&98+achC z)+%Tc-b#U|1QzY;)lYo`Y9h29e7XjofBeJDW%BIW5(nU8d$$p73$d*?2Edk$9enYL z2Z*#ZtswBK`%n>J*BZXp3w?c%&BB(=aCID74VVc*#tXUyws;2ku?~>2&|`ks-q*?Y zzRr5^SR_m=60X1deL4^A-P*AJTKTH`V614OUN2ZC%+7&rgQkH;g4cnZ7rHF4-TRUb zFj*m2l*ED_?iyP+#6iR%dlitiYLvk+Vb@N`WWjRpiFh2aHR#_BwhYt>I53aT?)ehN4Qm(xD2K;>I6`HRSB+YFrdWUT}T|^?eADAB4*A7k-cv%#W zoOrxpNwrS^0000T+ literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/42/backlogs.png b/plugin/buycourses/resources/img/42/backlogs.png new file mode 100644 index 0000000000000000000000000000000000000000..c7e3cdeeb81a2a6be6ec11c2086d47dec0c97880 GIT binary patch literal 2200 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/42/buysubscriptions.png b/plugin/buycourses/resources/img/42/buysubscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..359169ef617a13a8bae29a3f6340a407c6d2caf7 GIT binary patch literal 1263 zcmVwKexM1hP7#F&6XJX<;UF!ljhK*5IL=pvcV~C){L{SI^lwla!MwmxG>T&D1 zD7vS+r>DCI#y{!gR@b@z`R}>^sk$X}!J&8098lpHAl>5HQHv~d)d+x`?@b+lXV14Q z#hMSUYHq->2SmgJh!uZ2VrPPSbLVEZ&Ha9(CAs70FzZH_5veNi?Q3Xa$$Bme)tCfj z%adb=KbovS(Zmgn_om64c(X1q{lySeS5dV_PyL=?aZaDn#~1 zoFqAL9GfH=0N0@^B8e;O1My^?W8A!#^|Im$W#rD`XxMhc9wE21|XmFnA$aVSHjxS5b$T9 zlx*sQI1wh=w*S;mvn*Cq0mFHpsa<1~1NiXk2zzH<9h*iRrgpoH>v96W){@5ccvDpy z0_H9JoARTojC-whdwS~#3zf9BhVniDgBf`11&_EjKfVLgO@D@oHn;)SJE<941dMuM zB>gwk<&FRH#)lrN%fre;VZPrJF$+N>##jJBS9i_s!W~4ObH`Zts{T7;Ujj ziddJIAHbU7#<)849C3Xk)#5E)#CqdI16zA0ZQ>8j{aH?zYD^}QZbt|njy#?RXFzxs zc(D^nG_akZu=G-@4NrDXCcNi6oNUaqWWDEheXuGMHp5>`4VhUeVS);7SfMsFfho-( zwF+0C*hk{@_Cr@!sV?_NC%9n+fb9G%CJAtF6`8nxzMdhtYXQyxpQ^+2xzd$aH~jGV z)xxD8zv!w&6*3tRVYGNz4Dtivk-@5>%r;}TdB&h)7o$n{S zw*V8JgYFNL76i5Q6T|mDTN45D)V?C{cHaVO)zqtH|Kl|g03bUj-$&&L5cLeMR>02| zW&i$LYbF3dcJ2KHIMg-BT8LvX=1jl5t2`0e?#60-8*2Ig5s>I2NVwwtxGumgt{BsX Z{{XFxsll@FFLnR`002ovPDHLkV1faFN3H+> literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/42/discount.png b/plugin/buycourses/resources/img/42/discount.png new file mode 100644 index 0000000000000000000000000000000000000000..9846e3eff2fe640c7d3902c0bea4c5d8db63ba30 GIT binary patch literal 5070 zcmV;<6EW004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv 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

cq7 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 literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/42/settings.png b/plugin/buycourses/resources/img/42/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..975dc44dfdd5adc0830d4c48a20432ba5ab87eb1 GIT binary patch literal 3070 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 M07*qoM6N<$f-4m0xBvhE literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/42/subscriptionssettings.png b/plugin/buycourses/resources/img/42/subscriptionssettings.png new file mode 100644 index 0000000000000000000000000000000000000000..21f37ff019db9ff36684650041f37cb3beaa61a1 GIT binary patch literal 1680 zcmV;B25K~z|U&6itjTt^wle{=Ti&E9qFU3=}dBgctzHz*=OEwofL zRcVD#=mSt96$uH^0#7`E;E~WGR0tR$@j{Cbaat50K}0kp!ic5{O;yp9IIxZrCr-1D zZ)@+a_rB*cXLva4wKuU{0%^tn>6|m)I6k$G>!6&-wN20kri771|SCzc*l8R(o&r^nLFZEND3Ue16|1 z$8mHV`IUFG2M_@2z&AIeiJzhvSUGs{(~m|HANpWxAy?wS$ul!OH1ys)`FbZ@F#LOrUgJWk;0x^vBCnhpaP5Dm3~X}Y1D!M-xi0hlOxD8ipbnN;dpt? z3tX zzYBn(r82w^6}VP{s-*mpsAyjLw(dz-EE|kY%#bU%=Z};QV>f1*o?oonyfd9<{AQ~D zE>amhm&OrQ6M#z9WOQPN^kQk%9P1Joeq)ruBg6dh><9qk$rL~O)t~vS8ls~uOuQE3cKm;9;Qc@Yfx)3+o<20h=)?>F z-jlD<{Mm2ed-*ir3htTj9_4#KKUMen`t!fxYtOw*q2zAw7mlCf=|e-jec3$`|Mrh- zeEB;=y!6VOs}g9?fMMDw1(qcN5QeK{YKpcjiQ^~?bGb{JmW^dgZ0UBweciJ>*?)~r z@jCjg79N#zeDo83lA?s<{yPXXXrNLxDOF6Ots%E%*b^8%1wED`YQGza|v z6iQWW=@1E)@!$L*o>HO~SwM6E7XIb-fP%ng1EV{hukPvT4X#RH)f)W=5XLM`sl!mX z304Iv0t}!MfC><_q00u@3~il?^z?4=z=<)qO#^@U>qQ2KhM7ub0eJgzlEEXxoWFP- zfcb2JpB()Q$4>u!g_k0bJPgGt2=+i)gS-GkfNn#R33)FhLJ;bN)SRGboLlm4An?!e zDMqi)Fgu?I;GL-jMki*to}33@AzLJIZJLoQx9UFn;_GPTF)#{n;|46rBN&98+achC z)+%Tc-b#U|1QzY;)lYo`Y9h29e7XjofBeJDW%BIW5(nU8d$$p73$d*?2Edk$9enYL z2Z*#ZtswBK`%n>J*BZXp3w?c%&BB(=aCID74VVc*#tXUyws;2ku?~>2&|`ks-q*?Y zzRr5^SR_m=60X1deL4^A-P*AJTKTH`V614OUN2ZC%+7&rgQkH;g4cnZ7rHF4-TRUb zFj*m2l*ED_?iyP+#6iR%dlitiYLvk+Vb@N`WWjRpiFh2aHR#_BwhYt>I53aT?)ehN4Qm(xD2K;>I6`HRSB+YFrdWUT}T|^?eADAB4*A7k-cv%#W zoOrxpNwrS^0000T+ literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/64/backlogs.png b/plugin/buycourses/resources/img/64/backlogs.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c83f66c71a1f3678078c6e75e6195a6cfa4360 GIT binary patch literal 3253 zcmV;m3`+BfP)RrNxBQlS=8w1J*?vy!g!Z9{ztT^?cdzfg=bZ07_uTWlzn_(ea5+O)TY+k0 znt*C6P;E>TP;CXOjcEd^tw6OgO+d93s5YhvsI~&t#$``{J+|VxZ(h5?vF(OxC<9RM z%Jysjv1{L-OWr5~IzCj_@ZI}v*f6tk_M`_G%Gp@8vk0>RQO+mK287ux&COGHE@KRD z?>+i6V8xU>UnE8m;CM;x##|iFo%{%a#z`bV`GCe4jq)QL&$>8;Y zNDT@oJGC2of|pD4X?2FXdVZnqYV435Q$7S8g|4}QNZlS_heUpW1R-G%AEoHzVMdh( z?B%rHvLXK$5h5dmCV!8NVJLNk@X)O^H1BWfJuaQcpHP2uLL-o9rN3ejKoG?EH zJ~Sy|OhAC2tw6xVU;@(4TQi0MV*)aMNGPL?FZTo!YofR97jW!0lF7Z-^gN;NZoEyt zI~f(n5fFyv-6P;)AcZ9$fPu8kq_=l~x|oY+TX`=ikWmgRK8NECKVWIG6a#S+#eX8`6*8I+^7=n z3m-Z0bM={)&&#jQC$a(o24kiez~0TbYeLC*l)~it7(BjbTq9LMfnr@(W;vgk@zShx z5N$mDgj(Mha?g@G#IGc-A_1X4Sr3r&l%|M6XQPWTAaBm%`sbJ*?VOS-+Jis~i8#r4 zjlR=9EjM*A^h~ULw)@-$)_cXr({(6+Wv13G`!OsP_wO63WJemh1DI&j& z{R{5%Q-z?#IyaaB8=>=SzZor{7z0I(?LMv?cNKmXTo)XNB}ysEKMAOi2jmkWx$!IL z`YSQU;CUWeYvONi`;pxMWCzc1WnBa2 z9ieylEU~)ToXMu~asjA<=0@dbzyP5t8RKbw|{``SlU7g_sJ05=S zx_^j2{rZ1q?H12#Qa}*GceaHLiqt*0Le+@K@OcSv95>Pm@&t^A00Toq#3UkaS=83n zviy!axVpWa1N-;$oN|E&?di71c^ zz`_L!OZpV?yVw5!nPDu;V$IsMeC_LBXZ!Z;0KD?bE9~64lQnDCa_1Mnh#N&DlS!6+ z;M!3hGnl6IDBIutP|jv^(o|zc>gwW^ zmtSVbQ%~Vq7U@V@dIs3p++e<>$uEN-gyA%!o(ji#fQZmP(Eshh!GV$)To z@iGD^1u!FMrA$*X)?C%z&Xz4(M#=5z>EVq%dzjPIga)+8oYxN0V-P;|7Z%@K;n2B7 z?cepk{8xFuiXPD4-@jx2{Q3Wp&1LVp`l_qvx{h0#E6cX?x{2VrE|z5xhUd-?EX%@m z-BP}7TNcS=atzDGc|TVM$oXU5ZJ1Kz7Er@R^1b|Aj(PLuap=&YQd6xpOP4Msl}cem zFiH*0N}53dY+LyJO;JzA$S3}N{kZ@*AprnKjvV=hQtILE?(V;@t*xC05JgckJ(B*o zZA_~Xp=j89t39GrYbXXWhV|>#v3m7t?z!il68WoEt>X8uzDh2a%by^LKJ7>R;k57X z1l4C-=Arp3<>)w}A~NXwwsW##U4LT3jh*0UuUg;=dEhT${8k9LM3tkA9S^7A@l7zJ2`W`R56O zfRQL7EqagS(q91Hx|FZd3pvJ8yyU-m65LW_+c=g*B9Y*I*IdJ~qetoM>m!OH(osZO ztUZz&nUvfq5l~R39>@DzrS*L=Ok1oF?251OLSx7pLl%8ZjI1HZJ|-d)pJGm4t_}9b z@Vp0X3I8&omF;MaRw}>TM94-4Us-R8n1}GQ8zv?9B3-f(%eoaW`K)&1XI;+|&vnK1 zJkf6aEMD?iZ2RLt{8C)Mf?N-{4lns_i}o6V@}xv z%4Wm`7Gc}1X!izC^Fa<{{Fe#C1Hdu)6Z|(uVL7w_jo5Z0ma~-5>?g_{15N=wQ(D&V z!rBCIffz6XM1PL|LMfA7dVzj1axaO1{|~tAKarUxpxO#l8`A_-TY+k0nt*C6P;E>T nP;CXOjcEd^tw6PL*%R=8YW&XZk?xl~00000NkvXXu0mjfsvt6D literal 0 HcmV?d00001 diff --git a/plugin/buycourses/resources/img/64/buycourses.png b/plugin/buycourses/resources/img/64/buycourses.png new file mode 100644 index 0000000000000000000000000000000000000000..2f42065bda91e3d134c29a4d15b0bad078ee3c1f GIT binary patch literal 4067 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 N002ovPDHLkV1mA(q+ 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" + beneficiaryName + " - [ " + percentage + " % ]

"); + + } else if (type === 'renew') { + + $("#panelSliders").append("" + beneficiaryName + " - [ " + percentage + " % ]
"); + + } + + verifyPaypalAccountByBeneficiary(beneficiaryId); + + sliderCounter++; + stepSlide = count - 1; + + if (stepSlide === 0) { + (type === 'default') ? minPercentage = 0 : minPercentage = 100; + (type === 'default') ? stepSlide = 100 : stepSlide = 1; + } + + + $( "#panelSliders .panelSliders" ).slider({ + value: percentage, + min: minPercentage, + max: maxPercentage, + step: stepSlide, + animate: true, + slide: function( event, ui ) { + + $(this).prev(".value").text(ui.value); + + var total = 0; + var sliders = $( "#panelSliders .panelSliders" ); + + sliders.not(this).each(function() { + value = $(this).slider("option", "value"); + total += value; + }); + + total += ui.value; + var delta = 100 - total; + + sliders.not(this).each(function() { + + var t = $(this); + value = t.slider("option", "value"); + + var newValue = value + (delta/stepSlide); + + if (newValue < 0 || ui.value == 100) + newValue = 0; + if (newValue > 100) + newValue = 100; + + t.prev('.value').text(newValue); + t.slider('value', newValue); + + }); + + $("[name=\'commissions\']").val(getSlidersValues()); + } + }); + }); + + $("[name=\'commissions\']").val(getSlidersValues()); + showCorrectSliderHandler(); +}; + +function getSlidersValues() { + var commissions = ""; + + $( "#panelSliders .panelSliders" ).each(function() { + commissions += $(this).prev(".value").text() + ','; + }); + + commissions = commissions.substring(0, commissions.length-1); + + return commissions; +} + +function showCorrectSliderHandler() { + var correctHandler = []; + + $("#panelSliders > span").each(function () { + if ($(this).hasClass('value')) { + correctHandler.push($(this).text()); + } + }); + + var counter = 0; + + $("#panelSliders .panelSliders > span").each(function () { + if ($(this).hasClass('ui-slider-handle')) { + $(this).css('left', correctHandler[counter]+'%'); + counter++; + } + }); +} + +function verifyPaypalAccountByBeneficiary(userId) { + + return $.ajax({ + data: 'id='+userId, + url: 'buycourses.ajax.php?a=verifyPaypal', + type: 'POST', + success: function(response) { + $("#"+userId).append(' '+response); + } + }); +} diff --git a/plugin/buycourses/resources/js/modals.js b/plugin/buycourses/resources/js/modals.js new file mode 100644 index 000000000..90e677d1d --- /dev/null +++ b/plugin/buycourses/resources/js/modals.js @@ -0,0 +1 @@ +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file diff --git a/plugin/buycourses/src/buy_course_plugin.class.php b/plugin/buycourses/src/buy_course_plugin.class.php new file mode 100644 index 000000000..fca64a42d --- /dev/null +++ b/plugin/buycourses/src/buy_course_plugin.class.php @@ -0,0 +1,6146 @@ + + * @author Imanol Losada + * @author Alex Aragón + * @author Angel Fernando Quiroz Campos + * @author José Loguercio Silva + * @author Julio Montoya + */ +class BuyCoursesPlugin extends Plugin +{ + public const TABLE_PAYPAL = 'plugin_buycourses_paypal_account'; + public const TABLE_CURRENCY = 'plugin_buycourses_currency'; + public const TABLE_ITEM = 'plugin_buycourses_item'; + public const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary'; + public const TABLE_SALE = 'plugin_buycourses_sale'; + public const TABLE_TRANSFER = 'plugin_buycourses_transfer'; + public const TABLE_COMMISSION = 'plugin_buycourses_commission'; + public const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts'; + public const TABLE_SERVICES = 'plugin_buycourses_services'; + public const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale'; + public const TABLE_CULQI = 'plugin_buycourses_culqi'; + public const TABLE_GLOBAL_CONFIG = 'plugin_buycourses_global_config'; + public const TABLE_INVOICE = 'plugin_buycourses_invoices'; + public const TABLE_TPV_REDSYS = 'plugin_buycourses_tpvredsys_account'; + public const TABLE_COUPON = 'plugin_buycourses_coupon'; + public const TABLE_COUPON_ITEM = 'plugin_buycourses_coupon_rel_item'; + public const TABLE_COUPON_SERVICE = 'plugin_buycourses_coupon_rel_service'; + public const TABLE_SUBSCRIPTION = 'plugin_buycourses_subscription'; + public const TABLE_SUBSCRIPTION_SALE = 'plugin_buycourses_subscription_rel_sale'; + public const TABLE_SUBSCRIPTION_PERIOD = 'plugin_buycourses_subscription_period'; + public const TABLE_COUPON_SALE = 'plugin_buycourses_coupon_rel_sale'; + public const TABLE_COUPON_SERVICE_SALE = 'plugin_buycourses_coupon_rel_service_sale'; + public const TABLE_COUPON_SUBSCRIPTION_SALE = 'plugin_buycourses_coupon_rel_subscription_sale'; + public const TABLE_STRIPE = 'plugin_buycourses_stripe_account'; + public const TABLE_TPV_CECABANK = 'plugin_buycourses_cecabank_account'; + public const PRODUCT_TYPE_COURSE = 1; + public const PRODUCT_TYPE_SESSION = 2; + public const PRODUCT_TYPE_SERVICE = 3; + public const PAYMENT_TYPE_PAYPAL = 1; + public const PAYMENT_TYPE_TRANSFER = 2; + public const PAYMENT_TYPE_CULQI = 3; + public const PAYMENT_TYPE_TPV_REDSYS = 4; + public const PAYMENT_TYPE_STRIPE = 5; + public const PAYMENT_TYPE_TPV_CECABANK = 6; + public const PAYOUT_STATUS_CANCELED = 2; + public const PAYOUT_STATUS_PENDING = 0; + public const PAYOUT_STATUS_COMPLETED = 1; + public const SALE_STATUS_CANCELED = -1; + public const SALE_STATUS_PENDING = 0; + public const SALE_STATUS_COMPLETED = 1; + public const SERVICE_STATUS_PENDING = 0; + public const SERVICE_STATUS_COMPLETED = 1; + public const SERVICE_STATUS_CANCELLED = -1; + public const SERVICE_TYPE_USER = 1; + public const SERVICE_TYPE_COURSE = 2; + public const SERVICE_TYPE_SESSION = 3; + public const SERVICE_TYPE_LP_FINAL_ITEM = 4; + public const CULQI_INTEGRATION_TYPE = 'INTEG'; + public const CULQI_PRODUCTION_TYPE = 'PRODUC'; + public const TAX_APPLIES_TO_ALL = 1; + public const TAX_APPLIES_TO_ONLY_COURSE = 2; + public const TAX_APPLIES_TO_ONLY_SESSION = 3; + public const TAX_APPLIES_TO_ONLY_SERVICES = 4; + public const PAGINATION_PAGE_SIZE = 6; + public const COUPON_DISCOUNT_TYPE_PERCENTAGE = 1; + public const COUPON_DISCOUNT_TYPE_AMOUNT = 2; + public const COUPON_STATUS_ACTIVE = 1; + public const COUPON_STATUS_DISABLE = 0; + + public $isAdminPlugin = false; + + /** + * BuyCoursesPlugin constructor. + */ + public function __construct() + { + parent::__construct( + '7.1', + " + Jose Angel Ruiz - NoSoloRed (original author)
+ Francis Gonzales and Yannick Warnier - BeezNest (integration)
+ Alex Aragón - BeezNest (Design icons and css styles)
+ Imanol Losada - BeezNest (introduction of sessions purchase)
+ Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports)
+ José Loguercio Silva - BeezNest (Payouts and buy Services)
+ Julio Montoya + ", + [ + 'show_main_menu_tab' => 'boolean', + 'public_main_menu_tab' => 'boolean', + 'include_sessions' => 'boolean', + 'include_services' => 'boolean', + 'paypal_enable' => 'boolean', + 'transfer_enable' => 'boolean', + 'culqi_enable' => 'boolean', + 'commissions_enable' => 'boolean', + 'unregistered_users_enable' => 'boolean', + 'hide_free_text' => 'boolean', + 'hide_shopping_cart_from_course_catalogue' => 'boolean', + 'invoicing_enable' => 'boolean', + 'tax_enable' => 'boolean', + 'use_currency_symbol' => 'boolean', + 'tpv_redsys_enable' => 'boolean', + 'stripe_enable' => 'boolean', + 'cecabank_enable' => 'boolean', + ] + ); + } + + /** + * @return BuyCoursesPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Check if plugin is enabled. + * + * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled + */ + public function isEnabled(bool $checkEnabled = false): bool + { + return $this->get('paypal_enable') || $this->get('transfer_enable') || $this->get('culqi_enable') || $this->get('stripe_enable') || $this->get('cecabank_enable'); + } + + /** + * This method creates the tables required to this plugin. + */ + public function install() + { + $tablesToBeCompared = [ + self::TABLE_PAYPAL, + self::TABLE_TRANSFER, + self::TABLE_CULQI, + self::TABLE_ITEM_BENEFICIARY, + self::TABLE_ITEM, + self::TABLE_SALE, + self::TABLE_CURRENCY, + self::TABLE_COMMISSION, + self::TABLE_PAYPAL_PAYOUTS, + self::TABLE_SERVICES, + self::TABLE_SERVICES_SALE, + self::TABLE_GLOBAL_CONFIG, + self::TABLE_INVOICE, + self::TABLE_TPV_REDSYS, + self::TABLE_COUPON, + self::TABLE_COUPON_ITEM, + self::TABLE_COUPON_SERVICE, + self::TABLE_SUBSCRIPTION, + self::TABLE_SUBSCRIPTION_SALE, + self::TABLE_SUBSCRIPTION_PERIOD, + self::TABLE_COUPON_SALE, + self::TABLE_COUPON_SERVICE_SALE, + self::TABLE_COUPON_SUBSCRIPTION_SALE, + self::TABLE_STRIPE, + self::TABLE_TPV_CECABANK, + ]; + $em = Database::getManager(); + $cn = $em->getConnection(); + $sm = $cn->getSchemaManager(); + $tables = $sm->tablesExist($tablesToBeCompared); + + if ($tables) { + return false; + } + + require_once api_get_path(SYS_PLUGIN_PATH).'buycourses/database.php'; + } + + /** + * This method drops the plugin tables. + */ + public function uninstall() + { + $tablesToBeDeleted = [ + self::TABLE_PAYPAL, + self::TABLE_TRANSFER, + self::TABLE_CULQI, + self::TABLE_ITEM_BENEFICIARY, + self::TABLE_ITEM, + self::TABLE_SALE, + self::TABLE_CURRENCY, + self::TABLE_COMMISSION, + self::TABLE_PAYPAL_PAYOUTS, + self::TABLE_SERVICES_SALE, + self::TABLE_SERVICES, + self::TABLE_GLOBAL_CONFIG, + self::TABLE_INVOICE, + self::TABLE_TPV_REDSYS, + self::TABLE_COUPON, + self::TABLE_COUPON_ITEM, + self::TABLE_COUPON_SERVICE, + self::TABLE_SUBSCRIPTION, + self::TABLE_SUBSCRIPTION_SALE, + self::TABLE_SUBSCRIPTION_PERIOD, + self::TABLE_COUPON_SALE, + self::TABLE_COUPON_SERVICE_SALE, + self::TABLE_COUPON_SUBSCRIPTION_SALE, + self::TABLE_STRIPE, + ]; + + foreach ($tablesToBeDeleted as $tableToBeDeleted) { + $table = Database::get_main_table($tableToBeDeleted); + $sql = "DROP TABLE IF EXISTS $table"; + Database::query($sql); + } + $this->manageTab(false); + } + + public function update() + { + $table = self::TABLE_GLOBAL_CONFIG; + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'global_tax_perc'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD ( + sale_email varchar(255) NOT NULL, + global_tax_perc int unsigned NOT NULL, + tax_applies_to int unsigned NOT NULL, + tax_name varchar(255) NOT NULL, + seller_name varchar(255) NOT NULL, + seller_id varchar(255) NOT NULL, + seller_address varchar(255) NOT NULL, + seller_email varchar(255) NOT NULL, + next_number_invoice int unsigned NOT NULL, + invoice_series varchar(255) NOT NULL + )"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'info_email_extra'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD (info_email_extra TEXT NOT NULL)"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $table = self::TABLE_ITEM; + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $table = self::TABLE_SERVICES; + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD tax_perc int unsigned NULL"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $table = self::TABLE_SALE; + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD ( + price_without_tax decimal(10,2) NULL, + tax_perc int unsigned NULL, + tax_amount decimal(10,2) NULL, + invoice int unsigned NULL + )"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD ( + price_without_discount decimal(10,2) NULL, + discount_amount decimal(10,2) NULL + )"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $table = self::TABLE_SERVICES_SALE; + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'tax_perc'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD ( + price_without_tax decimal(10,2) NULL, + tax_perc int unsigned NULL, + tax_amount decimal(10,2) NULL, + invoice int unsigned NULL + )"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $sql = "SHOW COLUMNS FROM $table WHERE Field = 'price_without_discount'"; + $res = Database::query($sql); + + if (Database::num_rows($res) === 0) { + $sql = "ALTER TABLE $table ADD ( + price_without_discount decimal(10,2) NULL, + discount_amount decimal(10,2) NULL + )"; + $res = Database::query($sql); + if (!$res) { + echo Display::return_message($this->get_lang('ErrorUpdateFieldDB'), 'warning'); + } + } + + $table = self::TABLE_INVOICE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + sale_id int unsigned NOT NULL, + is_service int unsigned NOT NULL, + num_invoice int unsigned NOT NULL, + year int(4) unsigned NOT NULL, + serie varchar(255) NOT NULL, + date_invoice datetime NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_TPV_REDSYS; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + merchantcode varchar(255) NOT NULL, + terminal varchar(255) NOT NULL, + currency varchar(255) NOT NULL, + kc varchar(255) NOT NULL, + url_redsys varchar(255) NOT NULL, + url_redsys_sandbox varchar(255) NOT NULL, + sandbox int unsigned NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $sql = "SELECT * FROM $table"; + $res = Database::query($sql); + if (Database::num_rows($res) == 0) { + Database::insert($table, [ + 'url_redsys' => 'https://sis.redsys.es/sis/realizarPago', + 'url_redsys_sandbox' => 'https://sis-t.redsys.es:25443/sis/realizarPago', + ]); + } + + $table = self::TABLE_COUPON; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + code varchar(255) NOT NULL, + discount_type int unsigned NOT NULL, + discount_amount decimal(10, 2) NOT NULL, + valid_start datetime NOT NULL, + valid_end datetime NOT NULL, + delivered varchar(255) NOT NULL, + active tinyint NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_COUPON_ITEM; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + coupon_id int unsigned NOT NULL, + product_type int unsigned NOT NULL, + product_id int unsigned NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_COUPON_SERVICE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + coupon_id int unsigned NOT NULL, + service_id int unsigned NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_SUBSCRIPTION; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + product_type int unsigned NOT NULL, + product_id int unsigned NOT NULL, + duration int unsigned NOT NULL, + currency_id int unsigned NOT NULL, + price decimal(10, 2) NOT NULL, + tax_perc int unsigned, + PRIMARY KEY (product_type, product_id, duration) + )"; + Database::query($sql); + + $table = self::TABLE_SUBSCRIPTION_SALE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + currency_id int unsigned NOT NULL, + reference varchar(255) NOT NULL, + date datetime NOT NULL, + user_id int unsigned NOT NULL, + product_type int NOT NULL, + product_name varchar(255) NOT NULL, + product_id int unsigned NOT NULL, + price decimal(10,2) NOT NULL, + price_without_tax decimal(10,2) NULL, + tax_perc int unsigned NULL, + tax_amount decimal(10,2) NULL, + status int NOT NULL, + payment_type int NOT NULL, + invoice int NOT NULL, + price_without_discount decimal(10,2), + discount_amount decimal(10,2), + subscription_end datetime NOT NULL, + expired tinyint NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_SUBSCRIPTION_PERIOD; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + duration int unsigned NOT NULL, + name varchar(50) NOT NULL, + PRIMARY KEY (duration) + )"; + Database::query($sql); + + $table = self::TABLE_COUPON_SALE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + coupon_id int unsigned NOT NULL, + sale_id int unsigned NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_COUPON_SERVICE_SALE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + coupon_id int unsigned NOT NULL, + service_sale_id int unsigned NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_COUPON_SUBSCRIPTION_SALE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + coupon_id int unsigned NOT NULL, + sale_id int unsigned NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $table = self::TABLE_STRIPE; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + account_id varchar(255) NOT NULL, + secret_key varchar(255) NOT NULL, + endpoint_secret varchar(255) NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + $sql = "SELECT * FROM $table"; + $res = Database::query($sql); + if (Database::num_rows($res) == 0) { + Database::insert($table, [ + 'account_id' => '', + 'secret_key' => '', + 'endpoint_secret' => '', + ]); + } + + $table = self::TABLE_TPV_CECABANK; + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int unsigned NOT NULL AUTO_INCREMENT, + crypto_key varchar(255) NOT NULL, + merchant_id varchar(255) NOT NULL, + acquirer_bin varchar(255) NOT NULL, + terminal_id varchar(255) NOT NULL, + cypher varchar(255) NOT NULL, + exponent varchar(255) NOT NULL, + supported_payment varchar(255) NOT NULL, + url varchar(255) NOT NULL, + PRIMARY KEY (id) + )"; + Database::query($sql); + + Display::addFlash( + Display::return_message( + $this->get_lang('Updated'), + 'info', + false + ) + ); + + $fieldlabel = 'buycourses_company'; + $fieldtype = '1'; + $fieldtitle = $this->get_lang('Company'); + $fielddefault = ''; + UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); + + $fieldlabel = 'buycourses_vat'; + $fieldtype = '1'; + $fieldtitle = $this->get_lang('VAT'); + $fielddefault = ''; + UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); + + $fieldlabel = 'buycourses_address'; + $fieldtype = '1'; + $fieldtitle = $this->get_lang('Address'); + $fielddefault = ''; + UserManager::create_extra_field($fieldlabel, $fieldtype, $fieldtitle, $fielddefault); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses'); + exit; + } + + /** + * This function verify if the plugin is enable and return the price info for a course or session in the new grid + * catalog for 1.11.x , the main purpose is to show if a course or session is in sale it shows in the main platform + * course catalog so the old buycourses plugin catalog can be deprecated. + * + * @param int $productId course or session id + * @param int $productType course or session type + * + * @return mixed bool|string html + */ + public function buyCoursesForGridCatalogValidator(int $productId, int $productType) + { + $return = []; + $paypal = $this->get('paypal_enable') === 'true'; + $transfer = $this->get('transfer_enable') === 'true'; + $stripe = $this->get('stripe_enable') === 'true'; + $culqi = $this->get('culqi_enable') === 'true'; + $cecabank = $this->get('cecabank_enable') === 'true'; + $tpv_redsys = $this->get('tpv_redsys_enable') === 'true'; + $hideFree = $this->get('hide_free_text') === 'true'; + + if ($paypal || $transfer || $stripe || $culqi || $cecabank || $tpv_redsys) { + $item = $this->getItemByProduct($productId, $productType); + $html = '
'; + if ($item) { + $html .= ' + '.$item['total_price_formatted'].' + '; + $return['verificator'] = true; + } else { + if ($hideFree == false) { + $html .= ' + '.$this->get_lang('Free').' + '; + } + $return['verificator'] = false; + } + $html .= '
'; + $return['html'] = $html; + + return $return; + } + + return false; + } + + /** + * Return the buyCourses plugin button to buy the course. + * + * @return string $html + */ + public function returnBuyCourseButton(int $productId, int $productType) + { + $productId = $productId; + $productType = $productType; + $url = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process.php?i='.$productId.'&t='.$productType; + $buyButton = Display::returnFontAwesomeIcon('shopping-cart'); + if ($this->get('hide_shopping_cart_from_course_catalogue') === 'true') { + $buyButton = Display::returnFontAwesomeIcon('check').PHP_EOL.get_lang('Subscribe'); + } + $html = ''. + $buyButton.''; + + return $html; + } + + /** + * Get the currency for sales. + * + * @return array The selected currency. Otherwise return false + */ + public function getSelectedCurrency() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_CURRENCY), + [ + 'where' => ['status = ?' => true], + ], + 'first' + ); + } + + /** + * Get a list of currencies. + * + * @return array The currencies. Otherwise return false + */ + public function getCurrencies() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_CURRENCY) + ); + } + + /** + * Save the selected currency. + * + * @param int $selectedId The currency Id + */ + public function saveCurrency(int $selectedId) + { + $currencyTable = Database::get_main_table( + self::TABLE_CURRENCY + ); + + Database::update( + $currencyTable, + ['status' => 0] + ); + Database::update( + $currencyTable, + ['status' => 1], + ['id = ?' => $selectedId] + ); + } + + /** + * Save the PayPal configuration params. + * + * @return int Rows affected. Otherwise return false + */ + public function savePaypalParams(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_PAYPAL), + [ + 'username' => $params['username'], + 'password' => $params['password'], + 'signature' => $params['signature'], + 'sandbox' => isset($params['sandbox']), + ], + ['id = ?' => 1] + ); + } + + /** + * Gets the stored PayPal params. + * + * @return array + */ + public function getPaypalParams() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_PAYPAL), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Gets the stored TPV Redsys params. + * + * @return array + */ + public function getTpvRedsysParams() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_TPV_REDSYS), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Save the tpv Redsys configuration params. + * + * @return int Rows affected. Otherwise return false + */ + public function saveTpvRedsysParams(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_TPV_REDSYS), + [ + 'merchantcode' => $params['merchantcode'], + 'terminal' => $params['terminal'], + 'currency' => $params['currency'], + 'kc' => $params['kc'], + 'url_redsys' => $params['url_redsys'], + 'url_redsys_sandbox' => $params['url_redsys_sandbox'], + 'sandbox' => isset($params['sandbox']), + ], + ['id = ?' => 1] + ); + } + + /** + * Save Stripe configuration params. + * + * @return int Rows affected. Otherwise return false + */ + public function saveStripeParameters(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_STRIPE), + [ + 'account_id' => $params['account_id'], + 'secret_key' => $params['secret_key'], + 'endpoint_secret' => $params['endpoint_secret'], + ], + ['id = ?' => 1] + ); + } + + /** + * Gets the stored Stripe params. + * + * @return array + */ + public function getStripeParams() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_STRIPE), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Save a transfer account information. + * + * @param array $params The transfer account + * + * @return int Rows affected. Otherwise, return false + */ + public function saveTransferAccount(array $params) + { + return Database::insert( + Database::get_main_table(self::TABLE_TRANSFER), + [ + 'name' => $params['tname'], + 'account' => $params['taccount'], + 'swift' => $params['tswift'], + ] + ); + } + + /** + * Save email message information in transfer. + * + * @param array $params The transfer message + * + * @return int Rows affected. Otherwise return false + */ + public function saveTransferInfoEmail(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_GLOBAL_CONFIG), + ['info_email_extra' => $params['tinfo_email_extra']], + ['id = ?' => 1] + ); + } + + /** + * Gets message information for transfer email. + * + * @return array + */ + public function getTransferInfoExtra() + { + return Database::select( + 'info_email_extra AS tinfo_email_extra', + Database::get_main_table(self::TABLE_GLOBAL_CONFIG), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Get a list of transfer accounts. + * + * @return array + */ + public function getTransferAccounts() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_TRANSFER) + ); + } + + /** + * Remove a transfer account. + * + * @param int $id The transfer account ID + * + * @return int Rows affected. Otherwise return false + */ + public function deleteTransferAccount(int $id) + { + return Database::delete( + Database::get_main_table(self::TABLE_TRANSFER), + ['id = ?' => $id] + ); + } + + /** + * Get registered item data. + * + * @param int $itemId The item ID + * + * @return array + */ + public function getItem(int $itemId) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_ITEM), + [ + 'where' => ['id = ?' => $itemId], + ], + 'first' + ); + } + + /** + * Get the item data. + * + * @param int $productId The item ID + * @param int $itemType The item type + * + * @return array + */ + public function getItemByProduct(int $productId, int $itemType, array $coupon = null) + { + $buyItemTable = Database::get_main_table(self::TABLE_ITEM); + $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); + + $fakeItemFrom = " + $buyItemTable i + INNER JOIN $buyCurrencyTable c + ON i.currency_id = c.id + "; + + $product = Database::select( + ['i.*', 'c.iso_code'], + $fakeItemFrom, + [ + 'where' => [ + 'i.product_id = ? AND i.product_type = ?' => [ + $productId, + $itemType, + ], + ], + ], + 'first' + ); + + if (empty($product)) { + return false; + } + + $this->setPriceSettings($product, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon); + + return $product; + } + + /** + * Get registered item data. + * + * @param int $itemId The product ID + * @param int $productType The product type + * + * @return array + */ + public function getSubscriptionItem(int $itemId, int $productType) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION), + [ + 'where' => ['product_id = ? AND product_type = ?' => [ + $itemId, + $productType, + ], + ], + ], + 'first' + ); + } + + /** + * Get the item data. + * + * @param int $productId The item ID + * @param int $itemType The item type + * @param array $coupon Array with at least 'discount_type' and 'discount_amount' elements + * + * @return array + */ + public function getSubscriptionItemByProduct(int $productId, int $itemType, array $coupon = null) + { + $buySubscriptionItemTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); + + $fakeItemFrom = " + $buySubscriptionItemTable s + INNER JOIN $buyCurrencyTable c + ON s.currency_id = c.id + "; + + $item = Database::select( + ['s.*', 'c.iso_code'], + $fakeItemFrom, + [ + 'where' => [ + 's.product_id = ? AND s.product_type = ?' => [ + $productId, + $itemType, + ], + ], + ], + 'first' + ); + + if (empty($item)) { + return false; + } + + $this->setPriceSettings($item, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon); + + return $item; + } + + /** + * Get the item data. + * + * @param int $productId The item ID + * @param int $itemType The item type + * + * @return array + */ + public function getSubscriptionsItemsByProduct(int $productId, int $itemType) + { + $buySubscriptionItemTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); + + $fakeItemFrom = " + $buySubscriptionItemTable s + INNER JOIN $buyCurrencyTable c + ON s.currency_id = c.id + "; + + $items = Database::select( + ['s.*', 'c.iso_code'], + $fakeItemFrom, + [ + 'where' => [ + 's.product_id = ? AND s.product_type = ?' => [ + $productId, + $itemType, + ], + ], + ] + ); + + for ($i = 0; $i < count($items); $i++) { + $this->setPriceSettings($items[$i], self::TAX_APPLIES_TO_ONLY_COURSE); + } + + if (empty($items)) { + return false; + } + + return $items; + } + + /** + * Get registered item data by duration. + * + * @param int $duration The subscription duration + * + * @return array + */ + public function getSubscriptionsItemsByDuration(int $duration) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION), + [ + 'where' => [ + 'duration = ?' => [$duration], + ], + ] + ); + } + + /** + * List courses details from the configuration page. + * + * @return array + */ + public function getCourseList(int $first, int $maxResults) + { + return $this->getCourses($first, $maxResults); + } + + /** + * Lists current user session details, including each session course details. + * + * It can return the number of rows when $typeResult is 'count'. + * + * @param string $name Optional. The name filter. + * @param int $min Optional. The minimum price filter. + * @param int $max Optional. The maximum price filter. + * @param string $typeResult Optional. 'all', 'first' or 'count'. + * + * @return array|int + */ + public function getCatalogSessionList(int $start, int $end, string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all', $sessionCategory = 0) + { + $sessions = $this->filterSessionList($start, $end, $name, $min, $max, $typeResult, $sessionCategory); + + if ($typeResult === 'count') { + return $sessions; + } + + $sessionCatalog = []; + // loop through all sessions + foreach ($sessions as $session) { + $sessionCourses = $session->getCourses(); + + if (empty($sessionCourses)) { + continue; + } + + $item = $this->getItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION + ); + + if (empty($item)) { + continue; + } + + $sessionData = $this->getSessionInfo($session->getId()); + $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName(); + $sessionData['enrolled'] = $this->getUserStatusForSession( + api_get_user_id(), + $session + ); + $sessionData['courses'] = []; + + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); + + $sessionCourseData = [ + 'title' => $course->getTitle(), + 'coaches' => [], + ]; + + $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus( + $course, + Chamilo\CoreBundle\Entity\Session::COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $sessionCourseData['coaches'][] = $user->getCompleteName(); + } + $sessionData['courses'][] = $sessionCourseData; + } + + $sessionCatalog[] = $sessionData; + } + + return $sessionCatalog; + } + + /** + * Lists current user course details. + * + * @param string $name Optional. The name filter + * @param int $min Optional. The minimum price filter + * @param int $max Optional. The maximum price filter + * + * @return array|int + */ + public function getCatalogCourseList(int $first, int $pageSize, string $name = null, int $min = 0, int $max = 0, string $typeResult = 'all') + { + $courses = $this->filterCourseList($first, $pageSize, $name, $min, $max, $typeResult); + + if ($typeResult === 'count') { + return $courses; + } + + if (empty($courses)) { + return []; + } + + $courseCatalog = []; + foreach ($courses as $course) { + $item = $this->getItemByProduct( + $course->getId(), + self::PRODUCT_TYPE_COURSE + ); + + if (empty($item)) { + continue; + } + + $courseItem = [ + 'id' => $course->getId(), + 'title' => $course->getTitle(), + 'code' => $course->getCode(), + 'course_img' => null, + 'item' => $item, + 'teachers' => [], + 'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course), + ]; + + foreach ($course->getTeachers() as $courseUser) { + $teacher = $courseUser->getUser(); + $courseItem['teachers'][] = $teacher->getCompleteName(); + } + + // Check images + $possiblePath = api_get_path(SYS_COURSE_PATH); + $possiblePath .= $course->getDirectory(); + $possiblePath .= '/course-pic.png'; + + if (file_exists($possiblePath)) { + $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png'; + } + $courseCatalog[] = $courseItem; + } + + return $courseCatalog; + } + + /** + * Lists current user subscription session details, including each session course details. + * + * It can return the number of rows when $typeResult is 'count'. + * + * @param int $start Pagination start. + * @param int $end Pagination end. + * @param string $name Optional. The name filter. + * @param string $typeResult Optional. 'all', 'first' or 'count'. + * @param int $sessionCategory Optional. Session category id + * + * @return array|int + */ + public function getCatalogSubscriptionSessionList(int $start, int $end, string $name = null, string $typeResult = 'all', int $sessionCategory = 0) + { + $sessions = $this->filterSubscriptionSessionList($start, $end, $name, $typeResult, $sessionCategory); + + if ($typeResult === 'count') { + return $sessions; + } + + $sessionCatalog = []; + // loop through all sessions + foreach ($sessions as $session) { + $sessionCourses = $session->getCourses(); + + if (empty($sessionCourses)) { + continue; + } + + $item = $this->getSubscriptionItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION + ); + + if (empty($item)) { + continue; + } + + $sessionData = $this->getSubscriptionSessionInfo($session->getId()); + $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName(); + $sessionData['enrolled'] = $this->getUserStatusForSubscriptionSession( + api_get_user_id(), + $session + ); + $sessionData['courses'] = []; + + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); + + $sessionCourseData = [ + 'title' => $course->getTitle(), + 'coaches' => [], + ]; + + $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus( + $course, + Chamilo\CoreBundle\Entity\Session::COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $sessionCourseData['coaches'][] = $user->getCompleteName(); + } + $sessionData['courses'][] = $sessionCourseData; + } + + $sessionCatalog[] = $sessionData; + } + + return $sessionCatalog; + } + + /** + * Lists current user subscription course details. + * + * @param string $typeResult Optional. 'all', 'first' or 'count'. + * + * @return array|int + */ + public function getCatalogSubscriptionCourseList(int $first, int $pageSize, string $name = null, string $typeResult = 'all') + { + $courses = $this->filterSubscriptionCourseList($first, $pageSize, $name, $typeResult); + + if ($typeResult === 'count') { + return $courses; + } + + if (empty($courses)) { + return []; + } + + $courseCatalog = []; + foreach ($courses as $course) { + $item = $this->getSubscriptionItemByProduct( + $course->getId(), + self::PRODUCT_TYPE_COURSE + ); + + if (empty($item)) { + continue; + } + + $courseItem = [ + 'id' => $course->getId(), + 'title' => $course->getTitle(), + 'code' => $course->getCode(), + 'course_img' => null, + 'item' => $item, + 'teachers' => [], + 'enrolled' => $this->getUserStatusForSubscriptionCourse(api_get_user_id(), $course), + ]; + + foreach ($course->getTeachers() as $courseUser) { + $teacher = $courseUser->getUser(); + $courseItem['teachers'][] = $teacher->getCompleteName(); + } + + // Check images + $possiblePath = api_get_path(SYS_COURSE_PATH); + $possiblePath .= $course->getDirectory(); + $possiblePath .= '/course-pic.png'; + + if (file_exists($possiblePath)) { + $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png'; + } + $courseCatalog[] = $courseItem; + } + + return $courseCatalog; + } + + public function getPriceWithCurrencyFromIsoCode(float $price, string $isoCode): string + { + $useSymbol = $this->get('use_currency_symbol') === 'true'; + + $result = $isoCode.' '.$price; + if ($useSymbol) { + if ($isoCode === 'BRL') { + $symbol = 'R$'; + } else { + $symbol = Symfony\Component\Intl\Intl::getCurrencyBundle()->getCurrencySymbol($isoCode); + } + $result = $symbol.' '.$price; + } + + return $result; + } + + /** + * Get course info. + * + * @return array + */ + public function getCourseInfo(int $courseId, array $coupon = null) + { + $entityManager = Database::getManager(); + $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId); + + if (empty($course)) { + return []; + } + + $item = $this->getItemByProduct( + $course->getId(), + self::PRODUCT_TYPE_COURSE, + $coupon + ); + + if (empty($item)) { + return []; + } + + $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription') + ->findOneBy( + [ + 'cId' => $course->getId(), + 'sessionId' => 0, + ], + [ + 'descriptionType' => 'ASC', + ] + ); + + $globalParameters = $this->getGlobalParameters(); + $courseInfo = [ + 'id' => $course->getId(), + 'title' => $course->getTitle(), + 'description' => $courseDescription ? $courseDescription->getContent() : null, + 'code' => $course->getCode(), + 'visual_code' => $course->getVisualCode(), + 'teachers' => [], + 'item' => $item, + 'tax_name' => $globalParameters['tax_name'], + 'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE), + 'course_img' => null, + ]; + + $courseTeachers = $course->getTeachers(); + + foreach ($courseTeachers as $teachers) { + $user = $teachers->getUser(); + $teacher['id'] = $user->getId(); + $teacher['name'] = $user->getCompleteName(); + $courseInfo['teachers'][] = $teacher; + } + + $possiblePath = api_get_path(SYS_COURSE_PATH); + $possiblePath .= $course->getDirectory(); + $possiblePath .= '/course-pic.png'; + + if (file_exists($possiblePath)) { + $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png'; + } + + return $courseInfo; + } + + /** + * Get session info. + * + * @return array + */ + public function getSessionInfo(int $sessionId, array $coupon = null) + { + $entityManager = Database::getManager(); + $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId); + + if (empty($session)) { + return []; + } + + $item = $this->getItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION, + $coupon + ); + + if (empty($item)) { + return []; + } + + $sessionDates = SessionManager::parseSessionDates( + [ + 'display_start_date' => $session->getDisplayStartDate(), + 'display_end_date' => $session->getDisplayEndDate(), + 'access_start_date' => $session->getAccessStartDate(), + 'access_end_date' => $session->getAccessEndDate(), + 'coach_access_start_date' => $session->getCoachAccessStartDate(), + 'coach_access_end_date' => $session->getCoachAccessEndDate(), + ] + ); + + $globalParameters = $this->getGlobalParameters(); + $sessionInfo = [ + 'id' => $session->getId(), + 'name' => $session->getName(), + 'description' => $session->getDescription(), + 'dates' => $sessionDates, + 'courses' => [], + 'tax_name' => $globalParameters['tax_name'], + 'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION), + 'image' => null, + 'nbrCourses' => $session->getNbrCourses(), + 'nbrUsers' => $session->getNbrUsers(), + 'item' => $item, + 'duration' => $session->getDuration(), + ]; + + $fieldValue = new ExtraFieldValue('session'); + $sessionImage = $fieldValue->get_values_by_handler_and_field_variable( + $session->getId(), + 'image' + ); + + if (!empty($sessionImage)) { + $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value']; + } + + $sessionCourses = $session->getCourses(); + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); + $sessionCourseData = [ + 'title' => $course->getTitle(), + 'coaches' => [], + ]; + + $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus( + $course, + Chamilo\CoreBundle\Entity\Session::COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $coaches['id'] = $user->getUserId(); + $coaches['name'] = $user->getCompleteName(); + $sessionCourseData['coaches'][] = $coaches; + } + + $sessionInfo['courses'][] = $sessionCourseData; + } + + return $sessionInfo; + } + + /** + * Get course info. + * + * @return array + */ + public function getSubscriptionCourseInfo(int $courseId, array $coupon = null) + { + $entityManager = Database::getManager(); + $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId); + + if (empty($course)) { + return []; + } + + $item = $this->getSubscriptionItemByProduct( + $course->getId(), + self::PRODUCT_TYPE_COURSE, + $coupon + ); + + if (empty($item)) { + return []; + } + + $courseDescription = $entityManager->getRepository('ChamiloCourseBundle:CCourseDescription') + ->findOneBy( + [ + 'cId' => $course->getId(), + 'sessionId' => 0, + ], + [ + 'descriptionType' => 'ASC', + ] + ); + + $globalParameters = $this->getGlobalParameters(); + $courseInfo = [ + 'id' => $course->getId(), + 'title' => $course->getTitle(), + 'description' => $courseDescription ? $courseDescription->getContent() : null, + 'code' => $course->getCode(), + 'visual_code' => $course->getVisualCode(), + 'teachers' => [], + 'item' => $item, + 'tax_name' => $globalParameters['tax_name'], + 'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_COURSE), + 'course_img' => null, + ]; + + $courseTeachers = $course->getTeachers(); + + foreach ($courseTeachers as $teachers) { + $user = $teachers->getUser(); + $teacher['id'] = $user->getId(); + $teacher['name'] = $user->getCompleteName(); + $courseInfo['teachers'][] = $teacher; + } + + $possiblePath = api_get_path(SYS_COURSE_PATH); + $possiblePath .= $course->getDirectory(); + $possiblePath .= '/course-pic.png'; + + if (file_exists($possiblePath)) { + $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/course-pic.png'; + } + + return $courseInfo; + } + + /** + * Get session info. + * + * @param array $sessionId The session ID + * + * @return array + */ + public function getSubscriptionSessionInfo(int $sessionId, array $coupon = null) + { + $entityManager = Database::getManager(); + $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId); + + if (empty($session)) { + return []; + } + + $item = $this->getSubscriptionItemByProduct( + $session->getId(), + self::PRODUCT_TYPE_SESSION, + $coupon + ); + + if (empty($item)) { + return []; + } + + $sessionDates = SessionManager::parseSessionDates( + [ + 'display_start_date' => $session->getDisplayStartDate(), + 'display_end_date' => $session->getDisplayEndDate(), + 'access_start_date' => $session->getAccessStartDate(), + 'access_end_date' => $session->getAccessEndDate(), + 'coach_access_start_date' => $session->getCoachAccessStartDate(), + 'coach_access_end_date' => $session->getCoachAccessEndDate(), + ] + ); + + $globalParameters = $this->getGlobalParameters(); + $sessionInfo = [ + 'id' => $session->getId(), + 'name' => $session->getName(), + 'description' => $session->getDescription(), + 'dates' => $sessionDates, + 'courses' => [], + 'tax_name' => $globalParameters['tax_name'], + 'tax_enable' => $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SESSION), + 'image' => null, + 'nbrCourses' => $session->getNbrCourses(), + 'nbrUsers' => $session->getNbrUsers(), + 'item' => $item, + 'duration' => $session->getDuration(), + ]; + + $fieldValue = new ExtraFieldValue('session'); + $sessionImage = $fieldValue->get_values_by_handler_and_field_variable( + $session->getId(), + 'image' + ); + + if (!empty($sessionImage)) { + $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH).$sessionImage['value']; + } + + $sessionCourses = $session->getCourses(); + foreach ($sessionCourses as $sessionCourse) { + $course = $sessionCourse->getCourse(); + $sessionCourseData = [ + 'title' => $course->getTitle(), + 'coaches' => [], + ]; + + $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus( + $course, + Chamilo\CoreBundle\Entity\Session::COACH + ); + + foreach ($userCourseSubscriptions as $userCourseSubscription) { + $user = $userCourseSubscription->getUser(); + $coaches['id'] = $user->getUserId(); + $coaches['name'] = $user->getCompleteName(); + $sessionCourseData['coaches'][] = $coaches; + } + + $sessionInfo['courses'][] = $sessionCourseData; + } + + return $sessionInfo; + } + + /** + * Register a sale. + * + * @param int $itemId The product ID + * @param int $paymentType The payment type + * @param string $couponId The coupon ID + * + * @return bool + */ + public function registerSale(int $itemId, int $paymentType, string $couponId = null) + { + if (!in_array( + $paymentType, + [ + self::PAYMENT_TYPE_PAYPAL, + self::PAYMENT_TYPE_TRANSFER, + self::PAYMENT_TYPE_CULQI, + self::PAYMENT_TYPE_TPV_REDSYS, + self::PAYMENT_TYPE_STRIPE, + self::PAYMENT_TYPE_TPV_CECABANK, + ] + ) + ) { + return false; + } + + $entityManager = Database::getManager(); + $item = $this->getItem($itemId); + + if (empty($item)) { + return false; + } + + $productName = ''; + if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) { + $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']); + + if (empty($course)) { + return false; + } + + $productName = $course->getTitle(); + } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) { + $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']); + + if (empty($session)) { + return false; + } + + $productName = $session->getName(); + } + + if ($couponId != null) { + $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']); + } + + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if ($coupon != null) { + if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) { + $couponDiscount = $coupon['discount_amount']; + } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $item['price']; + } + $item['price'] = $item['price'] - $couponDiscount; + $price = $item['price']; + $priceWithoutTax = null; + $taxPerc = null; + $taxAmount = 0; + $taxEnable = $this->get('tax_enable') === 'true'; + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; + + if ($taxEnable && + ( + $taxAppliesTo == self::TAX_APPLIES_TO_ALL || + ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_COURSE && $item['product_type'] == self::PRODUCT_TYPE_COURSE) || + ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SESSION && $item['product_type'] == self::PRODUCT_TYPE_SESSION) + ) + ) { + $priceWithoutTax = $item['price']; + $globalTaxPerc = $globalParameters['global_tax_perc']; + $precision = 2; + $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc']; + $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision); + $price = $priceWithoutTax + $taxAmount; + } + + $values = [ + 'reference' => $this->generateReference( + api_get_user_id(), + $item['product_type'], + $item['product_id'] + ), + 'currency_id' => $item['currency_id'], + 'date' => api_get_utc_datetime(), + 'user_id' => api_get_user_id(), + 'product_type' => $item['product_type'], + 'product_name' => $productName, + 'product_id' => $item['product_id'], + 'price' => $price, + 'price_without_tax' => $priceWithoutTax, + 'tax_perc' => $taxPerc, + 'tax_amount' => $taxAmount, + 'status' => self::SALE_STATUS_PENDING, + 'payment_type' => $paymentType, + 'price_without_discount' => $priceWithoutDiscount, + 'discount_amount' => $couponDiscount, + ]; + + return Database::insert(self::TABLE_SALE, $values); + } + + /** + * Update the sale reference. + * + * @return bool + */ + public function updateSaleReference(int $saleId, string $saleReference) + { + $saleTable = Database::get_main_table(self::TABLE_SALE); + + return Database::update( + $saleTable, + ['reference' => $saleReference], + ['id = ?' => $saleId] + ); + } + + /** + * Get sale data by ID. + * + * @return array + */ + public function getSale(int $saleId) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SALE), + [ + 'where' => ['id = ?' => (int) $saleId], + ], + 'first' + ); + } + + /** + * Get sale data by reference. + * + * @return array + */ + public function getSaleFromReference(string $reference) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SALE), + [ + 'where' => ['reference = ?' => $reference], + ], + 'first' + ); + } + + /** + * Get a list of sales by the payment type. + * + * @param int $paymentType The payment type to filter (default : Paypal) + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByPaymentType(int $paymentType = self::PAYMENT_TYPE_PAYPAL) + { + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 's.payment_type = ? AND s.status = ?' => [ + $paymentType, + self::SALE_STATUS_COMPLETED, + ], + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get data of sales. + * + * @param int $saleId The sale id + * @param int $isService Check if a service + * + * @return array The sale data + */ + public function getDataSaleInvoice(int $saleId, int $isService) + { + if ($isService) { + $sale = $this->getServiceSale($saleId); + $sale['reference'] = $sale['reference']; + $sale['product_name'] = $sale['service']['name']; + $sale['payment_type'] = $sale['payment_type']; + $sale['user_id'] = $sale['buyer']['id']; + $sale['date'] = $sale['buy_date']; + } else { + $sale = $this->getSale($saleId); + } + + return $sale; + } + + /** + * Get data of invoice. + * + * @param int $saleId The sale id + * @param int $isService Check if a service + * + * @return array The invoice data + */ + public function getDataInvoice(int $saleId, int $isService) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_INVOICE), + [ + 'where' => [ + 'sale_id = ? AND ' => (int) $saleId, + 'is_service = ?' => (int) $isService, + ], + ], + 'first' + ); + } + + /** + * Get invoice numbering. + * + * @param int $saleId The sale id + * @param int $isService Check if a service + * + * @return string + */ + public function getNumInvoice(int $saleId, int $isService) + { + $dataInvoice = $this->getDataInvoice($saleId, $isService); + if (empty($dataInvoice)) { + return '-'; + } + + return $dataInvoice['serie'].$dataInvoice['year'].'/'.$dataInvoice['num_invoice']; + } + + /** + * Get currency data by ID. + * + * @param int $currencyId The currency ID + * + * @return array + */ + public function getCurrency(int $currencyId) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_CURRENCY), + [ + 'where' => ['id = ?' => $currencyId], + ], + 'first' + ); + } + + /** + * Complete sale process. Update sale status to completed. + * + * @return bool + */ + public function completeSale(int $saleId) + { + $sale = $this->getSale($saleId); + + if ($sale['status'] == self::SALE_STATUS_COMPLETED) { + return true; + } + + $saleIsCompleted = false; + switch ($sale['product_type']) { + case self::PRODUCT_TYPE_COURSE: + $course = api_get_course_info_by_id($sale['product_id']); + $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $course['code']); + break; + case self::PRODUCT_TYPE_SESSION: + SessionManager::subscribeUsersToSession( + $sale['product_id'], + [$sale['user_id']], + api_get_session_visibility($sale['product_id']), + false + ); + + $saleIsCompleted = true; + break; + } + + if ($saleIsCompleted) { + $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED); + if ($this->get('invoicing_enable') === 'true') { + $this->setInvoice($sale['id']); + } + } + + return $saleIsCompleted; + } + + /** + * Update sale status to canceled. + * + * @param int $saleId The sale ID + */ + public function cancelSale(int $saleId) + { + $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED); + } + + /** + * Get payment types. + */ + public function getPaymentTypes(bool $onlyActive = false): array + { + $types = [ + self::PAYMENT_TYPE_PAYPAL => 'PayPal', + self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer'), + self::PAYMENT_TYPE_CULQI => 'Culqi', + self::PAYMENT_TYPE_TPV_REDSYS => $this->get_lang('TpvPayment'), + self::PAYMENT_TYPE_STRIPE => 'Stripe', + self::PAYMENT_TYPE_TPV_CECABANK => $this->get_lang('TpvCecabank'), + ]; + + if (!$onlyActive) { + return $types; + } + + if ($this->get('paypal_enable') !== 'true') { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL]); + } + + if ($this->get('transfer_enable') !== 'true') { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER]); + } + + if ($this->get('culqi_enable') !== 'true') { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_CULQI]); + } + + if ($this->get('tpv_redsys_enable') !== 'true' + || !file_exists(api_get_path(SYS_PLUGIN_PATH).'buycourses/resources/apiRedsys.php') + ) { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS]); + } + + if ($this->get('stripe_enable') !== 'true') { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_STRIPE]); + } + + if ($this->get('cecabank_enable') !== 'true') { + unset($types[BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK]); + } + + return $types; + } + + /** + * Register a invoice. + * + * @param int $saleId The sale ID + * @param int $isService The service type to filter (default : 0) + */ + public function setInvoice(int $saleId, int $isService = 0) + { + $invoiceTable = Database::get_main_table(self::TABLE_INVOICE); + $year = date('Y'); + + $globalParameters = $this->getGlobalParameters(); + $numInvoice = $globalParameters['next_number_invoice']; + $serie = $globalParameters['invoice_series']; + + if (empty($numInvoice)) { + $item = Database::select( + ['MAX(num_invoice) AS num_invoice'], + $invoiceTable, + [ + 'where' => ['year = ?' => $year], + ], + 'first' + ); + + $numInvoice = 1; + if ($item !== false) { + $numInvoice = (int) ($item['num_invoice'] + 1); + } + } else { + Database::update( + Database::get_main_table(self::TABLE_GLOBAL_CONFIG), + ['next_number_invoice' => 0], + ['id = ?' => 1] + ); + } + + Database::insert( + $invoiceTable, + [ + 'sale_id' => $saleId, + 'is_service' => $isService, + 'num_invoice' => $numInvoice, + 'year' => $year, + 'serie' => $serie, + 'date_invoice' => api_get_utc_datetime(), + ] + ); + + // Record invoice in the sales table + $table = Database::get_main_table(self::TABLE_SALE); + if (!empty($isService)) { + $table = Database::get_main_table(self::TABLE_SERVICES_SALE); + } + + Database::update( + $table, + ['invoice' => 1], + ['id = ?' => $saleId] + ); + } + + /** + * Get Tax's types. + * + * @return array + */ + public function getTaxAppliesTo() + { + return [ + self::TAX_APPLIES_TO_ALL => $this->get_lang('AllCoursesSessionsAndServices'), + self::TAX_APPLIES_TO_ONLY_COURSE => $this->get_lang('OnlyCourses'), + self::TAX_APPLIES_TO_ONLY_SESSION => $this->get_lang('OnlySessions'), + self::TAX_APPLIES_TO_ONLY_SERVICES => $this->get_lang('OnlyServices'), + ]; + } + + /** + * Get a list of sales by the status. + * + * @param int $status The status to filter + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByStatus(int $status = self::SALE_STATUS_PENDING) + { + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => ['s.status = ?' => $status], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get the list statuses for sales. + * + * @throws Exception + * + * @return array + */ + public function getSaleListReport(string $dateStart = null, string $dateEnd = null) + { + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + $list = Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'order' => 'id DESC', + ] + ); + $listExportTemp = []; + $listExport = []; + $textStatus = null; + $paymentTypes = $this->getPaymentTypes(); + $productTypes = $this->getProductTypes(); + foreach ($list as $item) { + $statusSaleOrder = $item['status']; + switch ($statusSaleOrder) { + case 0: + $textStatus = $this->get_lang('SaleStatusPending'); + break; + case 1: + $textStatus = $this->get_lang('SaleStatusCompleted'); + break; + case -1: + $textStatus = $this->get_lang('SaleStatusCanceled'); + break; + } + $dateFilter = new DateTime($item['date']); + $listExportTemp[] = [ + 'id' => $item['id'], + 'reference' => $item['reference'], + 'status' => $textStatus, + 'status_filter' => $item['status'], + 'date' => $dateFilter->format('Y-m-d'), + 'order_time' => $dateFilter->format('H:i:s'), + 'price' => $item['iso_code'].' '.$item['price'], + 'product_type' => $productTypes[$item['product_type']], + 'product_name' => $item['product_name'], + 'payment_type' => $paymentTypes[$item['payment_type']], + 'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']), + 'email' => $item['email'], + ]; + } + $listExport[] = [ + get_lang('Number'), + $this->get_lang('OrderStatus'), + $this->get_lang('OrderDate'), + $this->get_lang('OrderTime'), + $this->get_lang('PaymentMethod'), + $this->get_lang('SalePrice'), + $this->get_lang('ProductType'), + $this->get_lang('ProductName'), + $this->get_lang('UserName'), + get_lang('Email'), + ]; + //Validation Export + $dateStart = strtotime($dateStart); + $dateEnd = strtotime($dateEnd); + foreach ($listExportTemp as $item) { + $dateFilter = strtotime($item['date']); + if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) { + $listExport[] = [ + 'id' => $item['id'], + 'status' => $item['status'], + 'date' => $item['date'], + 'order_time' => $item['order_time'], + 'payment_type' => $item['payment_type'], + 'price' => $item['price'], + 'product_type' => $item['product_type'], + 'product_name' => $item['product_name'], + 'complete_user_name' => $item['complete_user_name'], + 'email' => $item['email'], + ]; + } + } + + return $listExport; + } + + /** + * Get the statuses for sales. + * + * @return array + */ + public function getSaleStatuses() + { + return [ + self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'), + self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'), + self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'), + ]; + } + + /** + * Get the statuses for Payouts. + * + * @return array + */ + public function getPayoutStatuses() + { + return [ + self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'), + self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'), + self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted'), + ]; + } + + /** + * Get the list of product types. + * + * @return array + */ + public function getProductTypes() + { + return [ + self::PRODUCT_TYPE_COURSE => get_lang('Course'), + self::PRODUCT_TYPE_SESSION => get_lang('Session'), + ]; + } + + /** + * Get the list of service types. + * + * @return array + */ + public function getServiceTypes() + { + return [ + self::SERVICE_TYPE_USER => get_lang('User'), + self::SERVICE_TYPE_COURSE => get_lang('Course'), + self::SERVICE_TYPE_SESSION => get_lang('Session'), + self::SERVICE_TYPE_LP_FINAL_ITEM => get_lang('TemplateTitleCertificate'), + ]; + } + + /** + * Get the list of coupon status. + * + * @return array + */ + public function getCouponStatuses() + { + return [ + self::COUPON_STATUS_ACTIVE => $this->get_lang('CouponActive'), + self::COUPON_STATUS_DISABLE => $this->get_lang('CouponDisabled'), + ]; + } + + /** + * Get the list of coupon discount types. + * + * @return array + */ + public function getCouponDiscountTypes() + { + return [ + self::COUPON_DISCOUNT_TYPE_PERCENTAGE => $this->get_lang('CouponPercentage'), + self::COUPON_DISCOUNT_TYPE_AMOUNT => $this->get_lang('CouponAmount'), + ]; + } + + /** + * Generates a random text (used for order references). + * + * @param int $length Optional. Length of characters (defaults to 6) + * @param bool $lowercase Optional. Include lowercase characters + * @param bool $uppercase Optional. Include uppercase characters + * @param bool $numbers Optional. Include numbers + */ + public static function randomText( + int $length = 6, + bool $lowercase = true, + bool $uppercase = true, + bool $numbers = true + ): string { + $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : ''; + $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : ''; + $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : ''; + + if (strlen($salt) == 0) { + return ''; + } + + $str = ''; + + srand((float) microtime() * 1000000); + + for ($i = 0; $i < $length; $i++) { + $numbers = rand(0, strlen($salt) - 1); + $str .= substr($salt, $numbers, 1); + } + + return $str; + } + + /** + * Generates an order reference. + * + * @param int $userId The user ID + * @param int $productType The course/session type + * @param int $productId The course/session ID + */ + public function generateReference(int $userId, int $productType, int $productId): string + { + return vsprintf( + '%d-%d-%d-%s', + [$userId, $productType, $productId, self::randomText()] + ); + } + + /** + * Get a list of sales by the user. + * + * @param string $term The search term + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByUser(string $term) + { + $term = trim($term); + + if (empty($term)) { + return []; + } + + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.username LIKE %?% OR ' => $term, + 'u.lastname LIKE %?% OR ' => $term, + 'u.firstname LIKE %?%' => $term, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of sales by the user id. + * + * @param int $id The user id + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByUserId(int $id) + { + if (empty($id)) { + return []; + } + + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.id = ? AND s.status = ?' => [(int) $id, self::SALE_STATUS_COMPLETED], + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of sales by date range. + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByDate(string $dateStart, string $dateEnd) + { + $dateStart = trim($dateStart); + $dateEnd = trim($dateEnd); + if (empty($dateStart)) { + return []; + } + if (empty($dateEnd)) { + return []; + } + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 's.date BETWEEN ? AND ' => $dateStart, + ' ? ' => $dateEnd, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of sales by the user Email. + * + * @param string $term The search term + * + * @return array The sale list. Otherwise return false + */ + public function getSaleListByEmail(string $term) + { + $term = trim($term); + if (empty($term)) { + return []; + } + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.email LIKE %?% ' => $term, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Convert the course info to array with necessary course data for save item. + * + * @param array $defaultCurrency Optional. Currency data + * + * @return array + */ + public function getCourseForConfiguration(Course $course, array $defaultCurrency = null) + { + $courseItem = [ + 'item_id' => null, + 'course_id' => $course->getId(), + 'course_visual_code' => $course->getVisualCode(), + 'course_code' => $course->getCode(), + 'course_title' => $course->getTitle(), + 'course_directory' => $course->getDirectory(), + 'course_visibility' => $course->getVisibility(), + 'visible' => false, + 'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'], + 'price' => 0.00, + 'tax_perc' => null, + ]; + + $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE); + + if ($item !== false) { + $courseItem['item_id'] = $item['id']; + $courseItem['visible'] = true; + $courseItem['currency'] = $item['iso_code']; + $courseItem['price'] = $item['price']; + $courseItem['tax_perc'] = $item['tax_perc']; + } + + return $courseItem; + } + + /** + * Convert the session info to array with necessary session data for save item. + * + * @param Session $session The session data + * @param array $defaultCurrency Optional. Currency data + * + * @return array + */ + public function getSessionForConfiguration(Session $session, array $defaultCurrency = null) + { + $buyItemTable = Database::get_main_table(self::TABLE_ITEM); + $buyCurrencyTable = Database::get_main_table(self::TABLE_CURRENCY); + + $fakeItemFrom = " + $buyItemTable i + INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id + "; + + $sessionItem = [ + 'item_id' => null, + 'session_id' => $session->getId(), + 'session_name' => $session->getName(), + 'session_visibility' => $session->getVisibility(), + 'session_display_start_date' => null, + 'session_display_end_date' => null, + 'visible' => false, + 'currency' => empty($defaultCurrency) ? null : $defaultCurrency['iso_code'], + 'price' => 0.00, + 'tax_perc' => null, + ]; + + $displayStartDate = $session->getDisplayStartDate(); + + if (!empty($displayStartDate)) { + $sessionItem['session_display_start_date'] = api_format_date( + $session->getDisplayStartDate()->format('Y-m-d h:i:s') + ); + } + + $displayEndDate = $session->getDisplayEndDate(); + + if (!empty($displayEndDate)) { + $sessionItem['session_display_end_date'] = api_format_date( + $session->getDisplayEndDate()->format('Y-m-d h:i:s'), + DATE_TIME_FORMAT_LONG_24H + ); + } + + $item = Database::select( + ['i.*', 'c.iso_code'], + $fakeItemFrom, + [ + 'where' => [ + 'i.product_id = ? AND ' => $session->getId(), + 'i.product_type = ?' => self::PRODUCT_TYPE_SESSION, + ], + ], + 'first' + ); + + if ($item !== false) { + $sessionItem['item_id'] = $item['id']; + $sessionItem['visible'] = true; + $sessionItem['currency'] = $item['iso_code']; + $sessionItem['price'] = $item['price']; + $sessionItem['tax_perc'] = $item['tax_perc']; + } + + return $sessionItem; + } + + /** + * Get all beneficiaries for a item. + * + * @param int $itemId The item ID + * + * @return array The beneficiaries. Otherwise return false + */ + public function getItemBeneficiaries(int $itemId) + { + $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); + + return Database::select( + '*', + $beneficiaryTable, + [ + 'where' => [ + 'item_id = ?' => $itemId, + ], + ] + ); + } + + /** + * Delete a item with its beneficiaries. + * + * @param int $itemId The item ID + * + * @return int The number of affected rows. Otherwise return false + */ + public function deleteItem(int $itemId) + { + $itemTable = Database::get_main_table(self::TABLE_ITEM); + $affectedRows = Database::delete( + $itemTable, + ['id = ?' => $itemId] + ); + + if (!$affectedRows) { + return false; + } + + return $this->deleteItemBeneficiaries($itemId); + } + + /** + * Register a item. + * + * @param array $itemData The item data + * + * @return int The item ID. Otherwise return false + */ + public function registerItem(array $itemData) + { + $itemTable = Database::get_main_table(self::TABLE_ITEM); + + return Database::insert($itemTable, $itemData); + } + + /** + * Update the item data by product. + * + * @param array $itemData The item data to be updated + * @param int $productId The product ID + * @param int $productType The type of product + * + * @return int The number of affected rows. Otherwise return false + */ + public function updateItem(array $itemData, int $productId, int $productType) + { + $itemTable = Database::get_main_table(self::TABLE_ITEM); + + return Database::update( + $itemTable, + $itemData, + [ + 'product_id = ? AND ' => $productId, + 'product_type' => $productType, + ] + ); + } + + /** + * Remove all beneficiaries for a item. + * + * @param int $itemId The user ID + * + * @return int The number of affected rows. Otherwise return false + */ + public function deleteItemBeneficiaries(int $itemId) + { + $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); + + return Database::delete( + $beneficiaryTable, + ['item_id = ?' => $itemId] + ); + } + + /** + * Register the beneficiaries users with the sale of item. + * + * @param int $itemId The item ID + * @param array $userIds The beneficiary user ID and Teachers commissions if enabled + */ + public function registerItemBeneficiaries(int $itemId, array $userIds) + { + $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY); + + $this->deleteItemBeneficiaries($itemId); + + foreach ($userIds as $userId => $commissions) { + Database::insert( + $beneficiaryTable, + [ + 'item_id' => $itemId, + 'user_id' => (int) $userId, + 'commissions' => (int) $commissions, + ] + ); + } + } + + /** + * Check if a course is valid for sale. + * + * @param Course $course The course + * + * @return bool + */ + public function isValidCourse(Course $course) + { + $courses = $this->getCourses(); + + foreach ($courses as $_c) { + if ($_c->getCode() === $course->getCode()) { + return true; + } + } + + return false; + } + + /** + * Gets the beneficiaries with commissions and current paypal accounts by sale. + * + * @param int $saleId The sale ID + * + * @return array + */ + public function getBeneficiariesBySale(int $saleId) + { + $sale = $this->getSale($saleId); + $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']); + $itemBeneficiaries = $this->getItemBeneficiaries($item['id']); + + return $itemBeneficiaries; + } + + /** + * gets all payouts. + * + * @param int $status - default 0 - pending + * @param int $payoutId - for get an individual payout if want all then false + * + * @return array + */ + public function getPayouts( + int $status = self::PAYOUT_STATUS_PENDING, + int $payoutId = 0, + int $userId = 0 + ) { + $condition = ($payoutId) ? 'AND p.id = '.($payoutId) : ''; + $condition2 = ($userId) ? ' AND p.user_id = '.($userId) : ''; + $typeResult = ($condition) ? 'first' : 'all'; + $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); + $saleTable = Database::get_main_table(self::TABLE_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD); + $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + + $paypalExtraField = Database::select( + "*", + $extraFieldTable, + [ + 'where' => ['variable = ?' => 'paypal'], + ], + 'first' + ); + + if (!$paypalExtraField) { + return false; + } + + $innerJoins = " + INNER JOIN $userTable u ON p.user_id = u.id + INNER JOIN $saleTable s ON s.id = p.sale_id + INNER JOIN $currencyTable c ON s.currency_id = c.id + LEFT JOIN $extraFieldValues efv ON p.user_id = efv.item_id + AND field_id = ".((int) $paypalExtraField['id'])." + "; + + $payouts = Database::select( + "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code", + "$payoutsTable p $innerJoins", + [ + 'where' => ['p.status = ? '.$condition.' '.$condition2 => $status], + ], + $typeResult + ); + + return $payouts; + } + + /** + * Verify if the beneficiary have a paypal account. + * + * @return true if the user have a paypal account, false if not + */ + public function verifyPaypalAccountByBeneficiary(int $userId) + { + $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD); + $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + + $paypalExtraField = Database::select( + '*', + $extraFieldTable, + [ + 'where' => ['variable = ?' => 'paypal'], + ], + 'first' + ); + + if (!$paypalExtraField) { + return false; + } + + $paypalFieldId = $paypalExtraField['id']; + $paypalAccount = Database::select( + 'value', + $extraFieldValues, + [ + 'where' => ['field_id = ? AND item_id = ?' => [(int) $paypalFieldId, $userId]], + ], + 'first' + ); + + if (!$paypalAccount) { + return false; + } + + if ($paypalAccount['value'] === '') { + return false; + } + + return true; + } + + /** + * Register the users payouts. + * + * @return array + */ + public function storePayouts(int $saleId) + { + $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); + $platformCommission = $this->getPlatformCommission(); + + $sale = $this->getSale($saleId); + $commission = (int) $platformCommission['commission']; + $teachersCommission = number_format( + (floatval($sale['price']) * $commission) / 100, + 2 + ); + + $beneficiaries = $this->getBeneficiariesBySale($saleId); + foreach ($beneficiaries as $beneficiary) { + $beneficiaryCommission = (int) $beneficiary['commissions']; + Database::insert( + $payoutsTable, + [ + 'date' => $sale['date'], + 'payout_date' => api_get_utc_datetime(), + 'sale_id' => $saleId, + 'user_id' => $beneficiary['user_id'], + 'commission' => number_format( + (floatval($teachersCommission) * $beneficiaryCommission) / 100, + 2 + ), + 'status' => self::PAYOUT_STATUS_PENDING, + ] + ); + } + } + + /** + * Register the users payouts. + * + * @param int $saleId The subscription sale ID + * + * @return array + */ + public function storeSubscriptionPayouts(int $saleId) + { + $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); + $platformCommission = $this->getPlatformCommission(); + + $sale = $this->getSubscriptionSale($saleId); + $commission = (int) $platformCommission['commission']; + $teachersCommission = number_format( + (floatval($sale['price']) * $commission) / 100, + 2 + ); + + $beneficiaries = $this->getBeneficiariesBySale($saleId); + foreach ($beneficiaries as $beneficiary) { + $beneficiaryCommission = (int) $beneficiary['commissions']; + Database::insert( + $payoutsTable, + [ + 'date' => $sale['date'], + 'payout_date' => api_get_utc_datetime(), + 'sale_id' => $saleId, + 'user_id' => $beneficiary['user_id'], + 'commission' => number_format( + (floatval($teachersCommission) * $beneficiaryCommission) / 100, + 2 + ), + 'status' => self::PAYOUT_STATUS_PENDING, + ] + ); + } + } + + /** + * Register the users payouts. + * + * @param int $payoutId The payout ID + * @param int $status The status to set (-1 to cancel, 0 to pending, 1 to completed) + * + * @return array + */ + public function setStatusPayouts(int $payoutId, int $status) + { + $payoutsTable = Database::get_main_table(self::TABLE_PAYPAL_PAYOUTS); + + Database::update( + $payoutsTable, + ['status' => (int) $status], + ['id = ?' => (int) $payoutId] + ); + } + + /** + * Gets the stored platform commission params. + * + * @return array + */ + public function getPlatformCommission() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_COMMISSION), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Update the platform commission. + * + * @param array $params platform commission + * + * @return int The number of affected rows. Otherwise return false + */ + public function updateCommission(array $params) + { + $commissionTable = Database::get_main_table(self::TABLE_COMMISSION); + + return Database::update( + $commissionTable, + ['commission' => (int) $params['commission']] + ); + } + + /** + * Register additional service. + * + * @param array $service params + * + * @return mixed response + */ + public function storeService(array $service) + { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + + $return = Database::insert( + $servicesTable, + [ + 'name' => Security::remove_XSS($service['name']), + 'description' => Security::remove_XSS($service['description']), + 'price' => $service['price'], + 'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null, + 'duration_days' => (int) $service['duration_days'], + 'applies_to' => (int) $service['applies_to'], + 'owner_id' => (int) $service['owner_id'], + 'visibility' => (int) $service['visibility'], + 'image' => '', + 'video_url' => $service['video_url'], + 'service_information' => $service['service_information'], + ] + ); + + if ($return && !empty($service['picture_crop_image_base_64']) && + !empty($service['picture_crop_result']) + ) { + $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']); + $img = str_replace(' ', '+', $img); + $data = base64_decode($img); + $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$return.'.png'; + file_put_contents($file, $data); + + Database::update( + $servicesTable, + ['image' => 'simg-'.$return.'.png'], + ['id = ?' => $return] + ); + } + + return $return; + } + + /** + * update a service. + * + * @return mixed response + */ + public function updateService(array $service, int $id) + { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + if (!empty($service['picture_crop_image_base_64'])) { + $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']); + $img = str_replace(' ', '+', $img); + $data = base64_decode($img); + $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png'; + file_put_contents($file, $data); + } + + return Database::update( + $servicesTable, + [ + 'name' => Security::remove_XSS($service['name']), + 'description' => Security::remove_XSS($service['description']), + 'price' => $service['price'], + 'tax_perc' => $service['tax_perc'] != '' ? (int) $service['tax_perc'] : null, + 'duration_days' => (int) $service['duration_days'], + 'applies_to' => (int) $service['applies_to'], + 'owner_id' => (int) $service['owner_id'], + 'visibility' => (int) $service['visibility'], + 'image' => 'simg-'.$id.'.png', + 'video_url' => $service['video_url'], + 'service_information' => $service['service_information'], + ], + ['id = ?' => $id] + ); + } + + /** + * Remove a service. + * + * @param int $id The transfer account ID + * + * @return int Rows affected. Otherwise return false + */ + public function deleteService(int $id) + { + Database::delete( + Database::get_main_table(self::TABLE_SERVICES_SALE), + ['service_id = ?' => $id] + ); + + return Database::delete( + Database::get_main_table(self::TABLE_SERVICES), + ['id = ?' => $id] + ); + } + + /** + * @param array|null $coupon Array with at least 'discount_type' and 'discount_amount' elements + */ + public function setPriceSettings(array &$product, int $productType, array $coupon = null): bool + { + if (empty($product)) { + return false; + } + + $taxPerc = null; + $product['has_coupon'] = $coupon != null ? true : false; + $couponDiscount = 0; + if ($coupon != null) { + if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) { + $couponDiscount = $coupon['discount_amount']; + } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $couponDiscount = ($product['price'] * $coupon['discount_amount']) / 100; + } + $product['price_without_discount'] = $product['price']; + } + $product['discount_amount'] = $couponDiscount; + $product['price'] = $product['price'] - $couponDiscount; + $priceWithoutTax = $product['price']; + $product['total_price'] = $product['price']; + $product['tax_amount'] = 0; + + if ($this->checkTaxEnabledInProduct($productType)) { + if (is_null($product['tax_perc'])) { + $globalParameters = $this->getGlobalParameters(); + $globalTaxPerc = $globalParameters['global_tax_perc']; + $taxPerc = $globalTaxPerc; + } else { + $taxPerc = $product['tax_perc']; + } + //$taxPerc = is_null($product['tax_perc']) ? $globalTaxPerc : $product['tax_perc']; + + $taxAmount = round($priceWithoutTax * $taxPerc / 100, 2); + $product['tax_amount'] = $taxAmount; + $priceWithTax = $priceWithoutTax + $taxAmount; + $product['total_price'] = $priceWithTax; + } + + $product['tax_perc_show'] = $taxPerc; + $product['price_formatted'] = $this->getPriceWithCurrencyFromIsoCode( + $product['price'], + $product['iso_code'] + ); + + $product['tax_amount_formatted'] = number_format($product['tax_amount'], 2); + + $product['total_price_formatted'] = $this->getPriceWithCurrencyFromIsoCode( + $product['total_price'], + $product['iso_code'] + ); + + if ($coupon != null) { + $product['discount_amount_formatted'] = $this->getPriceWithCurrencyFromIsoCode( + $product['discount_amount'], + $product['iso_code'] + ); + + $product['price_without_discount_formatted'] = $this->getPriceWithCurrencyFromIsoCode( + $product['price_without_discount'], + $product['iso_code'] + ); + } + + return true; + } + + /** + * @param array $coupon + * + * @return array + */ + public function getService(int $id, array $coupon = null) + { + if (empty($id)) { + return []; + } + + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $conditions = ['WHERE' => ['s.id = ?' => $id]]; + $showData = 'first'; + $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id"; + $currency = $this->getSelectedCurrency(); + $isoCode = $currency['iso_code']; + $service = Database::select( + "s.*, '$isoCode' as currency, u.firstname, u.lastname", + "$servicesTable s $innerJoins", + $conditions, + $showData + ); + + $service['iso_code'] = $isoCode; + $globalParameters = $this->getGlobalParameters(); + + $this->setPriceSettings($service, self::TAX_APPLIES_TO_ONLY_SERVICES, $coupon); + + $service['tax_name'] = $globalParameters['tax_name']; + $service['tax_enable'] = $this->checkTaxEnabledInProduct(self::TAX_APPLIES_TO_ONLY_SERVICES); + $service['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']); + $service['image'] = !empty($service['image']) ? api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'] : null; + + return $service; + } + + /** + * List additional services. + * + * @return array + */ + public function getAllServices() + { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id"; + $return = Database::select( + 's.id', + "$servicesTable s $innerJoins", + [], + 'all' + ); + + $services = []; + foreach ($return as $index => $service) { + $services[$index] = $this->getService($service['id']); + } + + return $services; + } + + /** + * List additional services. + * + * @return array|int + */ + public function getServices(int $start, int $end, string $typeResult = 'all') + { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $conditions = ['limit' => "$start, $end"]; + $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id"; + $return = Database::select( + 's.id', + "$servicesTable s $innerJoins", + $conditions, + $typeResult + ); + + if ($typeResult === 'count') { + return $return; + } + + $services = []; + foreach ($return as $index => $service) { + $services[$index] = $this->getService($service['id']); + } + + return $services; + } + + /** + * Get the statuses for sales. + * + * @return array + */ + public function getServiceSaleStatuses() + { + return [ + self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'), + self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'), + self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted'), + ]; + } + + /** + * List services sales. + * + * @param int $buyerId buyer id + * @param int $status status + * @param int $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 ) + * @param int $nodeId the nodeId + * + * @return array + */ + public function getServiceSales( + int $buyerId = 0, + int $status = 0, + int $nodeType = 0, + int $nodeId = 0 + ) { + $conditions = null; + $groupBy = ''; + + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); + + $defaultOrder = 'id ASC'; + + if (!empty($buyerId)) { + $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => $defaultOrder]; + } + + if (is_numeric($status)) { + $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => $defaultOrder]; + } + + if ($buyerId) { + $conditions = ['WHERE' => ['ss.buyer_id = ?' => [$buyerId]], 'ORDER' => $defaultOrder]; + } + + if ($nodeType && $nodeId) { + $conditions = [ + 'WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]], + 'ORDER' => $defaultOrder, + ]; + } + + if ($nodeType && $nodeId && $buyerId && is_numeric($status)) { + $conditions = [ + 'WHERE' => [ + 'ss.node_type = ? AND ss.node_id = ? AND ss.buyer_id = ? AND ss.status = ?' => [ + $nodeType, + $nodeId, + $buyerId, + $status, + ], + ], + 'ORDER' => $defaultOrder, + ]; + } + + $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy"; + $return = Database::select( + 'DISTINCT ss.id ', + "$servicesSaleTable ss $innerJoins", + $conditions + //, "all", null, true + ); + + $list = []; + foreach ($return as $service) { + $list[] = $this->getServiceSale($service['id']); + } + + return $list; + } + + /** + * @param int $id service sale id + * + * @return array + */ + public function getServiceSale(int $id) + { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $servicesSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); + + if (empty($id)) { + return []; + } + + $conditions = ['WHERE' => ['ss.id = ?' => $id]]; + $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id "; + $currency = $this->getSelectedCurrency(); + $isoCode = $currency['iso_code']; + + $servicesSale = Database::select( + 'ss.*, s.name, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image', + "$servicesSaleTable ss $innerJoins", + $conditions, + 'first' + ); + $owner = api_get_user_info($servicesSale['owner_id']); + $buyer = api_get_user_info($servicesSale['buyer_id']); + + $servicesSale['service']['id'] = $servicesSale['service_id']; + $servicesSale['service']['name'] = $servicesSale['name']; + $servicesSale['service']['description'] = $servicesSale['description']; + $servicesSale['service']['price'] = $servicesSale['service_price']; + $servicesSale['service']['currency'] = $isoCode; + + $servicesSale['service']['total_price'] = $this->getPriceWithCurrencyFromIsoCode( + $servicesSale['price'], + $isoCode + ); + + $servicesSale['service']['duration_days'] = $servicesSale['duration_days']; + $servicesSale['service']['applies_to'] = $servicesSale['applies_to']; + $servicesSale['service']['owner']['id'] = $servicesSale['owner_id']; + $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']); + $servicesSale['service']['visibility'] = $servicesSale['visibility']; + $servicesSale['service']['image'] = $servicesSale['image']; + $servicesSale['item'] = $this->getService($servicesSale['service_id']); + $servicesSale['buyer']['id'] = $buyer['user_id']; + $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']); + $servicesSale['buyer']['username'] = $buyer['username']; + + return $servicesSale; + } + + /** + * Update service sale status to cancelled. + * + * @param int $serviceSaleId The sale ID + * + * @return bool + */ + public function cancelServiceSale(int $serviceSaleId) + { + $this->updateServiceSaleStatus( + $serviceSaleId, + self::SERVICE_STATUS_CANCELLED + ); + + return true; + } + + /** + * Complete service sale process. Update service sale status to completed. + * + * @param int $serviceSaleId The service sale ID + * + * @return bool + */ + public function completeServiceSale(int $serviceSaleId) + { + $serviceSale = $this->getServiceSale($serviceSaleId); + if ($serviceSale['status'] == self::SERVICE_STATUS_COMPLETED) { + return true; + } + + $this->updateServiceSaleStatus( + $serviceSaleId, + self::SERVICE_STATUS_COMPLETED + ); + + if ($this->get('invoicing_enable') === 'true') { + $this->setInvoice($serviceSaleId, 1); + } + + return true; + } + + /** + * Lists current service details. + * + * @return array|int + */ + public function getCatalogServiceList( + int $start, + int $end, + string $name = null, + int $min = 0, + int $max = 0, + $appliesTo = '', + string $typeResult = 'all' + ) { + $servicesTable = Database::get_main_table(self::TABLE_SERVICES); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $whereConditions = [ + 's.visibility <> ? ' => 0, + ]; + + if (!empty($name)) { + $whereConditions['AND s.name LIKE %?%'] = $name; + } + + if (!empty($min)) { + $whereConditions['AND s.price >= ?'] = $min; + } + + if (!empty($max)) { + $whereConditions['AND s.price <= ?'] = $max; + } + + if (!$appliesTo == '') { + $whereConditions['AND s.applies_to = ?'] = $appliesTo; + } + + $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id"; + $return = Database::select( + 's.*', + "$servicesTable s $innerJoins", + ['WHERE' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ($typeResult === 'count') { + return $return; + } + + $services = []; + foreach ($return as $index => $service) { + $services[$index] = $this->getService($service['id']); + } + + return $services; + } + + /** + * Register a Service sale. + * + * @param int $serviceId The service ID + * @param int $paymentType The payment type + * @param int $infoSelect The ID for Service Type + * + * @return bool + */ + public function registerServiceSale(int $serviceId, int $paymentType, int $infoSelect, int $couponId = null) + { + if (!in_array( + $paymentType, + [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI] + ) + ) { + return false; + } + + $userId = api_get_user_id(); + $service = $this->getService($serviceId); + + if (empty($service)) { + return false; + } + + if ($couponId != null) { + $coupon = $this->getCouponService($couponId, $serviceId); + } + + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if ($coupon != null) { + if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) { + $couponDiscount = $coupon['discount_amount']; + } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $couponDiscount = ($service['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $service['price']; + } + $service['price'] = $service['price'] - $couponDiscount; + $currency = $this->getSelectedCurrency(); + $price = $service['price']; + $priceWithoutTax = null; + $taxPerc = null; + $taxEnable = $this->get('tax_enable') === 'true'; + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; + $taxAmount = 0; + + if ($taxEnable && + ($taxAppliesTo == self::TAX_APPLIES_TO_ALL || $taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SERVICES) + ) { + $priceWithoutTax = $service['price']; + $globalTaxPerc = $globalParameters['global_tax_perc']; + $precision = 2; + $taxPerc = is_null($service['tax_perc']) ? $globalTaxPerc : $service['tax_perc']; + $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision); + $price = $priceWithoutTax + $taxAmount; + } + + $values = [ + 'service_id' => $serviceId, + 'reference' => $this->generateReference( + $userId, + $service['applies_to'], + $infoSelect + ), + 'currency_id' => $currency['id'], + 'price' => $price, + 'price_without_tax' => $priceWithoutTax, + 'tax_perc' => $taxPerc, + 'tax_amount' => $taxAmount, + 'node_type' => $service['applies_to'], + 'node_id' => $infoSelect, + 'buyer_id' => $userId, + 'buy_date' => api_get_utc_datetime(), + 'date_start' => api_get_utc_datetime(), + 'date_end' => date_format( + date_add( + date_create(api_get_utc_datetime()), + date_interval_create_from_date_string($service['duration_days'].' days') + ), + 'Y-m-d H:i:s' + ), + 'status' => self::SERVICE_STATUS_PENDING, + 'payment_type' => $paymentType, + 'price_without_discount' => $priceWithoutDiscount, + 'discount_amount' => $couponDiscount, + ]; + + $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values); + + return $returnedServiceSaleId; + } + + /** + * Save Culqi configuration params. + * + * @return int Rows affected. Otherwise return false + */ + public function saveCulqiParameters(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_CULQI), + [ + 'commerce_code' => $params['commerce_code'], + 'api_key' => $params['api_key'], + 'integration' => $params['integration'], + ], + ['id = ?' => 1] + ); + } + + /** + * Gets the stored Culqi params. + * + * @return array + */ + public function getCulqiParams() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_CULQI), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Save Cecabank configuration params. + * + * @return array + */ + public function saveCecabankParameters(array $params) + { + return Database::update( + Database::get_main_table(self::TABLE_TPV_CECABANK), + [ + 'crypto_key' => $params['crypto_key'], + 'merchant_id' => $params['merchart_id'], + 'acquirer_bin' => $params['acquirer_bin'], + 'terminal_id' => $params['terminal_id'], + 'cypher' => $params['cypher'], + 'exponent' => $params['exponent'], + 'supported_payment' => $params['supported_payment'], + 'url' => $params['url'], + ], + ['id = ?' => 1] + ); + } + + /** + * Gets the stored Cecabank params. + * + * @return array + */ + public function getCecabankParams() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_TPV_CECABANK), + ['id = ?' => 1], + 'first' + ); + } + + /** + * Save Global Parameters. + * + * @return int Rows affected. Otherwise return false + */ + public function saveGlobalParameters(array $params) + { + $sqlParams = [ + 'terms_and_conditions' => $params['terms_and_conditions'], + 'sale_email' => $params['sale_email'], + ]; + + if ($this->get('tax_enable') === 'true') { + $sqlParams['global_tax_perc'] = $params['global_tax_perc']; + $sqlParams['tax_applies_to'] = $params['tax_applies_to']; + $sqlParams['tax_name'] = $params['tax_name']; + } + + if ($this->get('invoicing_enable') === 'true') { + $sqlParams['seller_name'] = $params['seller_name']; + $sqlParams['seller_id'] = $params['seller_id']; + $sqlParams['seller_address'] = $params['seller_address']; + $sqlParams['seller_email'] = $params['seller_email']; + $sqlParams['next_number_invoice'] = $params['next_number_invoice']; + $sqlParams['invoice_series'] = $params['invoice_series']; + } + + return Database::update( + Database::get_main_table(self::TABLE_GLOBAL_CONFIG), + $sqlParams, + ['id = ?' => 1] + ); + } + + /** + * get Global Parameters. + * + * @return array + */ + public function getGlobalParameters() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_GLOBAL_CONFIG), + ['id = ?' => 1], + 'first' + ); + } + + /** + * @return bool + */ + public function checkTaxEnabledInProduct(int $productType) + { + if (empty($this->get('tax_enable') === 'true')) { + return false; + } + + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; + if ($taxAppliesTo == self::TAX_APPLIES_TO_ALL) { + return true; + } + + if ($taxAppliesTo == $productType) { + return true; + } + + return false; + } + + /** + * Get the path. + * + * @param string $var path variable + * + * @return string path + */ + public function getPath($var) + { + $pluginPath = api_get_path(WEB_PLUGIN_PATH).'buycourses/'; + $paths = [ + 'SERVICE_IMAGES' => $pluginPath.'uploads/services/images/', + 'SRC' => $pluginPath.'src/', + 'VIEW' => $pluginPath.'view/', + 'UPLOADS' => $pluginPath.'uploads/', + 'LANGUAGES' => $pluginPath.'lang/', + 'RESOURCES' => $pluginPath.'resources/', + 'RESOURCES_IMG' => $pluginPath.'resources/img/', + 'RESOURCES_CSS' => $pluginPath.'resources/css/', + 'RESOURCES_JS' => $pluginPath.'resources/js/', + ]; + + return $paths[$var]; + } + + /** + * @return array + */ + public function getBuyCoursePluginPrice(Session $session) + { + // start buycourse validation + // display the course price and buy button if the buycourses plugin is enabled and this course is configured + $isThisCourseInSale = $this->buyCoursesForGridCatalogValidator($session->getId(), self::PRODUCT_TYPE_SESSION); + $return = []; + + if ($isThisCourseInSale) { + // set the Price label + $return['html'] = $isThisCourseInSale['html']; + // set the Buy button instead register. + if ($isThisCourseInSale['verificator']) { + $return['buy_button'] = $this->returnBuyCourseButton($session->getId(), self::PRODUCT_TYPE_SESSION); + } + } + // end buycourse validation + return $return; + } + + /** + * Register a coupon sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID + * + * @return int + */ + public function registerCouponSale(int $saleId, int $couponId) + { + $sale = $this->getSale($saleId); + + if (empty($sale)) { + return false; + } + + $values = [ + 'coupon_id' => $couponId, + 'sale_id' => $saleId, + ]; + + return Database::insert(self::TABLE_COUPON_SALE, $values); + } + + /** + * Register a coupon service sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID + * + * @return int + */ + public function registerCouponServiceSale(int $saleId, int $couponId) + { + $sale = $this->getSale($saleId); + + if (empty($sale)) { + return false; + } + + $values = [ + 'coupon_id' => $couponId, + 'service_sale_id' => $saleId, + ]; + + return Database::insert(self::TABLE_COUPON_SERVICE_SALE, $values); + } + + /** + * Register a coupon sale. + * + * @param int $saleId The sale ID + * @param int $couponId The coupon ID + * + * @return int + */ + public function registerCouponSubscriptionSale(int $saleId, int $couponId) + { + $sale = $this->getSubscriptionSale($saleId); + + if (empty($sale)) { + return false; + } + + $values = [ + 'coupon_id' => (int) $couponId, + 'sale_id' => (int) $saleId, + ]; + + return Database::insert(self::TABLE_COUPON_SUBSCRIPTION_SALE, $values); + } + + /** + * Add a new coupon. + */ + public function addNewCoupon(array $coupon): bool + { + $couponId = $this->registerCoupon($coupon); + if ($couponId) { + if (isset($coupon['courses'])) { + foreach ($coupon['courses'] as $course) { + $this->registerCouponItem($couponId, self::PRODUCT_TYPE_COURSE, $course); + } + } + + if (isset($coupon['sessions'])) { + foreach ($coupon['sessions'] as $session) { + $this->registerCouponItem($couponId, self::PRODUCT_TYPE_SESSION, $session); + } + } + + if (isset($coupon['services'])) { + foreach ($coupon['services'] as $service) { + $this->registerCouponService($couponId, $service); + } + } + + return true; + } else { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponErrorInsert'), + 'error', + false + ) + ); + + return false; + } + } + + /** + * Add a new coupon. + * + * @return bool + */ + public function updateCouponData(array $coupon) + { + $this->updateCoupon($coupon); + $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_COURSE, $coupon['id']); + $this->deleteCouponItemsByCoupon(self::PRODUCT_TYPE_SESSION, $coupon['id']); + $this->deleteCouponServicesByCoupon($coupon['id']); + + if (isset($coupon['courses'])) { + foreach ($coupon['courses'] as $course) { + $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_COURSE, $course); + } + } + + if (isset($coupon['sessions'])) { + foreach ($coupon['sessions'] as $session) { + $this->registerCouponItem($coupon['id'], self::PRODUCT_TYPE_SESSION, $session); + } + } + + if (isset($coupon['services'])) { + foreach ($coupon['services'] as $service) { + $this->registerCouponService($coupon['id'], $service); + } + } + + return true; + } + + /** + * Update coupons delivered. + * + * @param int $couponId The coupon ID + * + * @return bool + */ + public function updateCouponDelivered(int $couponId) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + + $sql = "UPDATE $couponTable + SET delivered = delivered+1 + WHERE id = $couponId"; + + Database::query($sql); + } + + /** + * Get coupon info. + * + * @param int $couponId The coupon ID + * + * @return array The coupon data + */ + public function getCouponInfo(int $couponId) + { + $coupon = $this->getDataCoupon($couponId); + + $couponRelCourses = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_COURSE); + $couponRelSessions = $this->getItemsCoupons($couponId, self::PRODUCT_TYPE_SESSION); + $couponRelServices = $this->getServicesCoupons($couponId); + + $coupon['courses'] = $couponRelCourses; + $coupon['sessions'] = $couponRelSessions; + $coupon['services'] = $couponRelServices; + + return $coupon; + } + + /** + * Get a list of coupons. + * + * @param int $status The coupons activation status + * + * @return array Coupons data + */ + public function getCouponsListByStatus(int $status) + { + $coupons = $this->getDataCoupons($status); + + return $coupons; + } + + /** + * Get the coupon data. + * + * @return array The coupon data + */ + public function getCoupon(int $couponId, int $productType, int $productId) + { + $coupon = $this->getDataCoupon($couponId, $productType, $productId); + + return $coupon; + } + + /** + * Get data of the coupon code. + * + * @param string $couponCode The coupon code + * @param int $productId The product ID + * @param int $productType The product type + * + * @return array The coupon data + */ + public function getCouponByCode(string $couponCode, int $productType = null, int $productId = null) + { + $coupon = $this->getDataCouponByCode($couponCode, $productType, $productId); + + return $coupon; + } + + /** + * Get data of the coupon code for a service. + * + * @param int $couponId The coupon ID + * @param int $serviceId The product ID + * + * @return array The coupon data + */ + public function getCouponService(int $couponId, int $serviceId) + { + $coupon = $this->getDataCouponService($couponId, $serviceId); + + return $coupon; + } + + /** + * Get data of the coupon code for a service. + * + * @param string $couponCode The coupon code code + * @param int $serviceId The product id + * + * @return array The coupon data + */ + public function getCouponServiceByCode(string $couponCode, int $serviceId) + { + $coupon = $this->getDataCouponServiceByCode($couponCode, $serviceId); + + return $coupon; + } + + /** + * Get the coupon code of a item sale. + * + * @param int $saleId The sale ID + * + * @return string The coupon code + */ + public function getSaleCouponCode(int $saleId) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponSaleTable = Database::get_main_table(self::TABLE_COUPON_SALE); + + $couponFrom = " + $couponTable c + INNER JOIN $couponSaleTable s + on c.id = s.coupon_id + "; + + $couponCode = Database::select( + ['c.code'], + $couponFrom, + [ + 'where' => [ + 's.sale_id = ? ' => $saleId, + ], + ], + 'first' + ); + + return $couponCode['code']; + } + + /** + * Get the coupon code of a service sale. + * + * @param int $serviceSaleId The service sale ID + * + * @return string The coupon code + */ + public function getServiceSaleCouponCode(int $serviceSaleId) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponServiceSaleTable = Database::get_main_table(self::TABLE_COUPON_SERVICE_SALE); + + $couponFrom = " + $couponTable c + INNER JOIN $couponServiceSaleTable s + on c.id = s.coupon_id + "; + + $couponCode = Database::select( + ['c.code'], + $couponFrom, + [ + 'where' => [ + 's.service_sale_id = ? ' => $serviceSaleId, + ], + ], + 'first' + ); + + return $couponCode['code']; + } + + /** + * @return array + */ + public function getCecabankSignature(string $saleReference, float $price) + { + $urlOk = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/cecabank_success.php'; + $urlKo = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/cecabank_cancel.php'; + + $cecabankParams = $this->getCecabankParams(); + $signature = $cecabankParams['crypto_key'] + .$cecabankParams['merchant_id'] + .$cecabankParams['acquirer_bin'] + .$cecabankParams['terminal_id'] + .$saleReference + .$price * 100 + .'978' + .$cecabankParams['exponent'] + .$cecabankParams['cypher'] + .$urlOk + .$urlKo; + + $sha256 = hash('sha256', $signature); + $signature = strtolower($sha256); + + return $signature; + } + + /** + * Register a subscription sale. + * + * @param int $productId The product ID + * @param int $productType The product type + * @param int $paymentType The payment type + * @param int $duration The subscription duration + * @param int $couponId The coupon ID + * + * @return int + */ + public function registerSubscriptionSale( + int $productId, + int $productType, + int $paymentType, + int $duration, + int $couponId = null + ) { + if (!in_array( + $paymentType, + [ + self::PAYMENT_TYPE_PAYPAL, + self::PAYMENT_TYPE_TRANSFER, + self::PAYMENT_TYPE_CULQI, + self::PAYMENT_TYPE_TPV_REDSYS, + self::PAYMENT_TYPE_STRIPE, + self::PAYMENT_TYPE_TPV_CECABANK, + ] + ) + ) { + return false; + } + + $entityManager = Database::getManager(); + $item = $this->getSubscriptionItem($productId, $productType); + + if (empty($item)) { + return false; + } + + $productName = ''; + if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) { + $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']); + + if (empty($course)) { + return false; + } + + $productName = $course->getTitle(); + } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) { + $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']); + + if (empty($session)) { + return false; + } + + $productName = $session->getName(); + } + + if ($couponId != null) { + $coupon = $this->getCoupon($couponId, $item['product_type'], $item['product_id']); + } + + $couponDiscount = 0; + $priceWithoutDiscount = 0; + if ($coupon != null) { + if ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_AMOUNT) { + $couponDiscount = $coupon['discount_amount']; + } elseif ($coupon['discount_type'] == self::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $couponDiscount = ($item['price'] * $coupon['discount_amount']) / 100; + } + $priceWithoutDiscount = $item['price']; + } + $item['price'] = $item['price'] - $couponDiscount; + $price = $item['price']; + $priceWithoutTax = null; + $taxPerc = null; + $taxAmount = 0; + $taxEnable = $this->get('tax_enable') === 'true'; + $globalParameters = $this->getGlobalParameters(); + $taxAppliesTo = $globalParameters['tax_applies_to']; + + if ($taxEnable && + ( + $taxAppliesTo == self::TAX_APPLIES_TO_ALL || + ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_COURSE && $item['product_type'] == self::PRODUCT_TYPE_COURSE) || + ($taxAppliesTo == self::TAX_APPLIES_TO_ONLY_SESSION && $item['product_type'] == self::PRODUCT_TYPE_SESSION) + ) + ) { + $priceWithoutTax = $item['price']; + $globalTaxPerc = $globalParameters['global_tax_perc']; + $precision = 2; + $taxPerc = is_null($item['tax_perc']) ? $globalTaxPerc : $item['tax_perc']; + $taxAmount = round($priceWithoutTax * $taxPerc / 100, $precision); + $price = $priceWithoutTax + $taxAmount; + } + + $subscriptionEnd = date('y:m:d', strtotime('+'.$duration.' days')); + + $values = [ + 'reference' => $this->generateReference( + api_get_user_id(), + $item['product_type'], + $item['product_id'] + ), + 'currency_id' => $item['currency_id'], + 'date' => api_get_utc_datetime(), + 'user_id' => api_get_user_id(), + 'product_type' => $item['product_type'], + 'product_name' => $productName, + 'product_id' => $item['product_id'], + 'price' => $price, + 'price_without_tax' => $priceWithoutTax, + 'tax_perc' => $taxPerc, + 'tax_amount' => $taxAmount, + 'status' => self::SALE_STATUS_PENDING, + 'payment_type' => $paymentType, + 'price_without_discount' => $priceWithoutDiscount, + 'discount_amount' => $couponDiscount, + 'subscription_end' => $subscriptionEnd, + ]; + + return Database::insert(self::TABLE_SUBSCRIPTION_SALE, $values); + } + + /** + * Add a new subscription. + * + * @return bool + */ + public function addNewSubscription(array $subscription) + { + $result = false; + + if (isset($subscription['frequencies'])) { + foreach ($subscription['frequencies'] as $frequency) { + $subscriptionDb = $this->getSubscription($subscription['product_type'], $subscription['product_id'], $frequency['duration']); + + if (!isset($subscriptionDb) || empty($subscription)) { + Display::addFlash( + Display::return_message( + $this->get_lang('SubscriptionAlreadyExists').' ('.$frequency['duration'].')', + 'error', + false + ) + ); + + return false; + } else { + $subscriptionId = $this->registerSubscription($subscription, $frequency); + if ($subscriptionId) { + $result = true; + } else { + Display::addFlash( + Display::return_message( + $this->get_lang('SubscriptionErrorInsert'), + 'error', + false + ) + ); + + return false; + } + } + } + } else { + Display::addFlash( + Display::return_message( + $this->get_lang('FrequenciesNotSetError'), + 'error', + false + ) + ); + + return false; + } + + return $result; + } + + /** + * Add a new subscription. + * + * @return bool + */ + public function updateSubscriptions(int $productType, int $productId, int $taxPerc) + { + $this->updateSubscription($productType, $productId, $taxPerc); + } + + /** + * Delete a subscription. + * + * @return int + */ + public function deleteSubscription(int $productType, int $productId, int $duration) + { + return Database::delete( + Database::get_main_table(self::TABLE_SUBSCRIPTION), + [ + 'product_type = ? AND ' => (int) $productType, + 'product_id = ? AND ' => (int) $productId, + 'duration = ? ' => (int) $duration, + ] + ); + } + + /** + * Get a list of subscriptions by product ID and type. + * + * @param string $productId The product ID + * @param int $productType The product type + * + * @return array Subscriptions data + */ + public function getSubscriptions($productType, $productId) + { + $subscriptions = $this->getDataSubscriptions($productType, $productId); + + return $subscriptions; + } + + /** + * Get data of the subscription. + * + * @return array The subscription data + */ + public function getSubscription(int $productType, int $productId, int $duration, array $coupon = null) + { + $subscription = $this->getDataSubscription($productType, $productId, $duration); + + $currency = $this->getSelectedCurrency(); + $isoCode = $currency['iso_code']; + + $subscription['iso_code'] = $isoCode; + + $this->setPriceSettings($subscription, self::TAX_APPLIES_TO_ONLY_COURSE, $coupon); + + return $subscription; + } + + /** + * Get subscription sale data by ID. + * + * @param int $saleId The sale ID + * + * @return array + */ + public function getSubscriptionSale(int $saleId) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE), + [ + 'where' => ['id = ?' => $saleId], + ], + 'first' + ); + } + + /** + * Complete subscription sale process. Update sale status to completed. + * + * @param int $saleId The subscription sale ID + * + * @return bool + */ + public function completeSubscriptionSale(int $saleId) + { + $sale = $this->getSubscriptionSale($saleId); + + if ($sale['status'] == self::SALE_STATUS_COMPLETED) { + return true; + } + + $saleIsCompleted = false; + switch ($sale['product_type']) { + case self::PRODUCT_TYPE_COURSE: + $course = api_get_course_info_by_id($sale['product_id']); + $saleIsCompleted = CourseManager::subscribeUser($sale['user_id'], $course['code']); + break; + case self::PRODUCT_TYPE_SESSION: + SessionManager::subscribeUsersToSession( + $sale['product_id'], + [$sale['user_id']], + api_get_session_visibility($sale['product_id']), + false + ); + + $saleIsCompleted = true; + break; + } + + if ($saleIsCompleted) { + $this->updateSubscriptionSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED); + if ($this->get('invoicing_enable') === 'true') { + $this->setInvoice($sale['id']); + } + } + + return $saleIsCompleted; + } + + /** + * Update subscription sale status to canceled. + * + * @param int $saleId The subscription sale ID + */ + public function cancelSubscriptionSale(int $saleId) + { + $this->updateSubscriptionSaleStatus($saleId, self::SALE_STATUS_CANCELED); + } + + /** + * Get a list of subscription sales by the status. + * + * @param int $status The status to filter + * + * @return array The sale list. Otherwise return false + */ + public function getSubscriptionSaleListByStatus(int $status = self::SALE_STATUS_PENDING) + { + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => ['s.status = ?' => $status], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get the list statuses for subscriptions sales. + * + * @param string $dateStart + * @param string $dateEnd + * + * @throws Exception + * + * @return array + */ + public function getSubscriptionSaleListReport(string $dateStart = null, string $dateEnd = null) + { + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + $list = Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'order' => 'id DESC', + ] + ); + $listExportTemp = []; + $listExport = []; + $textStatus = null; + $paymentTypes = $this->getPaymentTypes(); + $productTypes = $this->getProductTypes(); + foreach ($list as $item) { + $statusSaleOrder = $item['status']; + switch ($statusSaleOrder) { + case 0: + $textStatus = $this->get_lang('SaleStatusPending'); + break; + case 1: + $textStatus = $this->get_lang('SaleStatusCompleted'); + break; + case -1: + $textStatus = $this->get_lang('SaleStatusCanceled'); + break; + } + $dateFilter = new DateTime($item['date']); + $listExportTemp[] = [ + 'id' => $item['id'], + 'reference' => $item['reference'], + 'status' => $textStatus, + 'status_filter' => $item['status'], + 'date' => $dateFilter->format('Y-m-d'), + 'order_time' => $dateFilter->format('H:i:s'), + 'price' => $item['iso_code'].' '.$item['price'], + 'product_type' => $productTypes[$item['product_type']], + 'product_name' => $item['product_name'], + 'payment_type' => $paymentTypes[$item['payment_type']], + 'complete_user_name' => api_get_person_name($item['firstname'], $item['lastname']), + 'email' => $item['email'], + ]; + } + $listExport[] = [ + get_lang('Number'), + $this->get_lang('OrderStatus'), + $this->get_lang('OrderDate'), + $this->get_lang('OrderTime'), + $this->get_lang('PaymentMethod'), + $this->get_lang('SalePrice'), + $this->get_lang('ProductType'), + $this->get_lang('ProductName'), + $this->get_lang('UserName'), + get_lang('Email'), + ]; + //Validation Export + $dateStart = strtotime($dateStart); + $dateEnd = strtotime($dateEnd); + foreach ($listExportTemp as $item) { + $dateFilter = strtotime($item['date']); + if (($dateFilter >= $dateStart) && ($dateFilter <= $dateEnd)) { + $listExport[] = [ + 'id' => $item['id'], + 'status' => $item['status'], + 'date' => $item['date'], + 'order_time' => $item['order_time'], + 'payment_type' => $item['payment_type'], + 'price' => $item['price'], + 'product_type' => $item['product_type'], + 'product_name' => $item['product_name'], + 'complete_user_name' => $item['complete_user_name'], + 'email' => $item['email'], + ]; + } + } + + return $listExport; + } + + /** + * Get a list of subscription sales by the user. + * + * @param string $term The search term + * + * @return array The sale list. Otherwise return false + */ + public function getSubscriptionSaleListByUser(string $term) + { + $term = trim($term); + + if (empty($term)) { + return []; + } + + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.username LIKE %?% OR ' => $term, + 'u.lastname LIKE %?% OR ' => $term, + 'u.firstname LIKE %?%' => $term, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of subscription sales by the user id. + * + * @param int $id The user id + * + * @return array The sale list. Otherwise return false + */ + public function getSubscriptionSaleListByUserId(int $id) + { + if (empty($id)) { + return []; + } + + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.id = ? AND s.status = ?' => [$id, self::SALE_STATUS_COMPLETED], + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of subscription sales by date range. + * + * @return array The sale list. Otherwise return false + */ + public function getSubscriptionSaleListByDate(string $dateStart, string $dateEnd) + { + $dateStart = trim($dateStart); + $dateEnd = trim($dateEnd); + if (empty($dateStart)) { + return []; + } + if (empty($dateEnd)) { + return []; + } + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 's.date BETWEEN ? AND ' => $dateStart, + ' ? ' => $dateEnd, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get a list of subscription sales by the user Email. + * + * @param string $term The search term + * + * @return array The sale list. Otherwise return false + */ + public function getSubscriptionSaleListByEmail(string $term) + { + $term = trim($term); + if (empty($term)) { + return []; + } + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + $currencyTable = Database::get_main_table(self::TABLE_CURRENCY); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $innerJoins = " + INNER JOIN $currencyTable c ON s.currency_id = c.id + INNER JOIN $userTable u ON s.user_id = u.id + "; + + return Database::select( + ['c.iso_code', 'u.firstname', 'u.lastname', 'u.email', 's.*'], + "$saleTable s $innerJoins", + [ + 'where' => [ + 'u.email LIKE %?% ' => $term, + ], + 'order' => 'id DESC', + ] + ); + } + + /** + * Get subscription sale data by ID. + * + * @param string $date The date + * + * @return array + */ + public function getSubscriptionsDue(string $date) + { + return Database::select( + 'id, user_id, product_id, product_type', + Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE), + [ + 'where' => ['subscription_end < ? AND status <> ? AND (expired is NULL OR expired <> ?)' => [ + $date, + self::SALE_STATUS_COMPLETED, + 1, + ], + ], + ], + 'first' + ); + } + + /** + * Get subscription sale data by ID. + * + * @param int $userId The user ID + * @param int $productId The product ID + * @param int $productType The product type + * + * @return array + */ + public function checkItemSubscriptionActive(int $userId, int $productId, int $productType) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE), + [ + 'where' => ['subscription_end >= ? AND userId = ? AND productId = ? AND productType = ? AND status <> ?' => [ + api_get_utc_datetime(), + $userId, + $productId, + $productType, + self::SALE_STATUS_COMPLETED, + ], + ], + ], + 'first' + ); + } + + /** + * Get subscription sale data by ID. + * + * @return array + */ + public function updateSubscriptionSaleExpirationStatus(int $id) + { + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + + return Database::update( + $saleTable, + ['expired' => 1], + ['id = ?' => $id] + ); + } + + /** + * Get the list of frequencies discount types. + * + * @return array + */ + public function getFrequencies() + { + $data = Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD), + [] + ); + + $frequenciesList = $this->getFrequenciesList(); + $frequencies = []; + + foreach ($data as $key => $items) { + $frequencies[$items['duration']] = $items['name']; + } + + return $frequencies; + } + + /** + * Get the list of frequencies discount types. + * + * @return array + */ + public function getFrequenciesList() + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD), + [] + ); + } + + /** + * Get the a frequency. + * + * @param int $duration The duration of the frequency value + * + * @return array + */ + public function selectFrequency(int $duration) + { + return Database::select( + '*', + Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD), + [ + 'where' => [ + 'duration = ?' => [ + (int) $duration, + ], + ], + ], + 'first' + ); + } + + /** + * Add a new subscription frequency. + * + * @return array + */ + public function addFrequency(int $duration, string $name) + { + $values = [ + 'duration' => $duration, + 'name' => $name, + ]; + + return Database::insert(self::TABLE_SUBSCRIPTION_PERIOD, $values); + } + + /** + * Update a subscription frequency. + * + * @return array + */ + public function updateFrequency(int $duration, string $name) + { + $periodTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD); + + return Database::update( + $periodTable, + ['name' => $name], + ['duration = ?' => $duration] + ); + } + + /** + * Delete a subscription frequency. + * + * @return array + */ + public function deleteFrequency(int $duration) + { + return Database::delete( + Database::get_main_table(self::TABLE_SUBSCRIPTION_PERIOD), + [ + 'duration = ?' => $duration, + ] + ); + } + + /** + * @return string + */ + public function getSubscriptionSuccessMessage(array $saleInfo) + { + switch ($saleInfo['product_type']) { + case self::PRODUCT_TYPE_COURSE: + $courseInfo = api_get_course_info_by_id($saleInfo['product_id']); + $url = api_get_course_url($courseInfo['code']); + break; + case self::PRODUCT_TYPE_SESSION: + $sessionId = (int) $saleInfo['product_id']; + $url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId; + break; + default: + $url = '#'; + } + + return Display::return_message( + sprintf( + $this->get_lang('SubscriptionToCourseXSuccessful'), + $url, + $saleInfo['product_name'] + ), + 'success', + false + ); + } + + /** + * @return string + */ + public static function returnPagination( + string $baseUrl, + string $currentPage, + string $pagesCount, + string $totalItems, + array $extraQueryParams = [] + ) { + $queryParams = HttpRequest::createFromGlobals()->query->all(); + + unset($queryParams['page']); + + $url = $baseUrl.'?'.http_build_query( + array_merge($queryParams, $extraQueryParams) + ); + + return Display::getPagination($url, $currentPage, $pagesCount, $totalItems); + } + + /** + * Returns the javascript to set the sales report table for courses. + */ + public static function getSalesReportScript(array $sales = [], bool $invoicingEnable = false) + { + $cols = " + '".preg_replace("/'/", "\\'", get_plugin_lang('OrderReference', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('OrderStatus', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('OrderDate', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('PaymentMethod', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('Price', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('CouponDiscount', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('Coupon', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('ProductType', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_plugin_lang('Name', 'BuyCoursesPlugin'))."', + '".preg_replace("/'/", "\\'", get_lang('UserName'))."', + '".preg_replace("/'/", "\\'", get_lang('Email'))."',"; + $model = " + {name:'reference', index:'reference', height:'auto', width:70, sorttype:'string', align:'center'}, + {name:'status', index:'status', height:'auto', width:70, sorttype:'string', align:'center'}, + {name:'date', index:'date', height:'auto', width:70, sorttype:'date', align:'center'}, + {name:'payment_type', index:'payment_type', height:'auto', width:70, sorttype:'string', align:'center'}, + {name:'total_price', index:'total_price', height:'auto', width:70, sorttype:'string', align:'center'}, + {name:'coupon_discount', index:'coupon_discount', height:'auto', width:40, sorttype:'string', align: 'center'}, + {name:'coupon', index:'coupon', height:'auto', width:60, sorttype:'string', align:'center'}, + {name:'product_type', index:'product_type', height:'auto', width:40, sorttype:'string'}, + {name:'product_name', index:'product_name', height:'auto', /*width:60,*/ sorttype:'string'}, + {name:'complete_user_name', index:'complete_user_name', height:'auto', width:70, sorttype:'string'}, + {name:'email', index:'email', height:'auto', /*width:60,*/ sorttype:'string'}, "; + if ($invoicingEnable) { + $model .= "{name:'invoice', index:'invoice', height:'auto', width:70, sorttype:'string'},"; + $cols .= "'".get_plugin_lang('Invoice', 'BuyCoursesPlugin')."',"; + } + $cols .= "'".get_lang('Options')."',"; + $model .= " + {name:'options', index:'options', height:'auto', width:60, sortable:false},"; + $data = ''; + foreach ($sales as $item) { + $option = ''; + if (!isset($item['complete_user_name'])) { + $item['complete_user_name'] = api_get_person_name($item['firstname'], $item['lastname']); + } + if ($item['invoice'] == 1) { + if ($invoicingEnable) { + $item['invoice'] = "". + Display::return_icon('default.png', get_plugin_lang('InvoiceView', 'BuyCoursesPlugin'), '', ICON_SIZE_MEDIUM). + "
".$item['num_invoice']. + "
"; + } + } else { + $item['invoice'] = null; + } + if ($item['status'] == BuyCoursesPlugin::SALE_STATUS_CANCELED) { + $item['status'] = get_plugin_lang('SaleStatusCanceled', 'BuyCoursesPlugin'); + } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_PENDING) { + $item['status'] = get_plugin_lang('SaleStatusPending', 'BuyCoursesPlugin'); + $option = ""; + } elseif ($item['status'] == BuyCoursesPlugin::SALE_STATUS_COMPLETED) { + $item['status'] = get_plugin_lang('SaleStatusCompleted', 'BuyCoursesPlugin'); + } + $item['options'] = $option; + $item['date'] = api_get_local_time($item['date']); + $data .= json_encode($item).","; + } + + return " +"; + } + + /** + * Filter the registered courses for show in plugin catalog. + */ + private function getCourses(int $first, int $maxResults) + { + $em = Database::getManager(); + $urlId = api_get_current_access_url_id(); + + $qb = $em->createQueryBuilder(); + $qb2 = $em->createQueryBuilder(); + $qb3 = $em->createQueryBuilder(); + + $qb = $qb + ->select('c') + ->from('ChamiloCoreBundle:Course', 'c') + ->where( + $qb->expr()->notIn( + 'c', + $qb2 + ->select('course2') + ->from('ChamiloCoreBundle:SessionRelCourse', 'sc') + ->join('sc.course', 'course2') + ->innerJoin( + 'ChamiloCoreBundle:AccessUrlRelSession', + 'us', + Join::WITH, + 'us.sessionId = sc.session' + )->where( + $qb->expr()->eq('us.accessUrlId ', $urlId) + ) + ->getDQL() + ) + )->andWhere( + $qb->expr()->in( + 'c', + $qb3 + ->select('course3') + ->from('ChamiloCoreBundle:AccessUrlRelCourse', 'uc') + ->join('uc.course', 'course3') + ->where( + $qb3->expr()->eq('uc.url ', $urlId) + ) + ->getDQL() + ) + ) + ->setFirstResult($first) + ->setMaxResults($maxResults); + + return $qb; + } + + /** + * Get the user status for the session. + * + * @param int $userId The user ID + * @param Session $session The session + * + * @return string + */ + private function getUserStatusForSession(int $userId, Session $session) + { + if (empty($userId)) { + return 'NO'; + } + + $entityManager = Database::getManager(); + $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser'); + + $buySaleTable = Database::get_main_table(self::TABLE_SALE); + + // Check if user bought the course + $sale = Database::select( + 'COUNT(1) as qty', + $buySaleTable, + [ + 'where' => [ + 'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [ + $userId, + self::PRODUCT_TYPE_SESSION, + $session->getId(), + self::SALE_STATUS_PENDING, + ], + ], + ], + 'first' + ); + + if ($sale['qty'] > 0) { + return 'TMP'; + } + + // Check if user is already subscribe to session + $userSubscription = $scuRepo->findBy([ + 'session' => $session, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; + } + + /** + * Get the user status for the course. + * + * @param int $userId The user Id + * @param Course $course The course + * + * @return string + */ + private function getUserStatusForCourse(int $userId, Course $course) + { + if (empty($userId)) { + return 'NO'; + } + + $entityManager = Database::getManager(); + $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser'); + $buySaleTable = Database::get_main_table(self::TABLE_SALE); + + // Check if user bought the course + $sale = Database::select( + 'COUNT(1) as qty', + $buySaleTable, + [ + 'where' => [ + 'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [ + $userId, + self::PRODUCT_TYPE_COURSE, + $course->getId(), + self::SALE_STATUS_PENDING, + ], + ], + ], + 'first' + ); + + if ($sale['qty'] > 0) { + return 'TMP'; + } + + // Check if user is already subscribe to course + $userSubscription = $cuRepo->findBy([ + 'course' => $course, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; + } + + /** + * Update the sale status. + * + * @param int $saleId The sale ID + * @param int $newStatus The new status + * + * @return bool + */ + private function updateSaleStatus(int $saleId, int $newStatus = self::SALE_STATUS_PENDING) + { + $saleTable = Database::get_main_table(self::TABLE_SALE); + + return Database::update( + $saleTable, + ['status' => (int) $newStatus], + ['id = ?' => (int) $saleId] + ); + } + + /** + * Search filtered sessions by name, and range of price. + * + * @param string $name Optional. The name filter + * @param int $min Optional. The minimum price filter + * @param int $max Optional. The maximum price filter + * @param string $typeResult Optional. 'all' and 'count' + * @param int $sessionCategory Optional. Session category id + * + * @return array + */ + private function filterSessionList( + int $start, + int $end, + string $name = null, + int $min = 0, + int $max = 0, + string $typeResult = 'all', + int $sessionCategory = 0 + ) { + $itemTable = Database::get_main_table(self::TABLE_ITEM); + $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); + + $innerJoin = "$itemTable i ON s.id = i.product_id"; + $whereConditions = [ + 'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION, + ]; + + if (!empty($name)) { + $whereConditions['AND s.name LIKE %?%'] = $name; + } + + if (!empty($min)) { + $whereConditions['AND i.price >= ?'] = $min; + } + + if (!empty($max)) { + $whereConditions['AND i.price <= ?'] = $max; + } + + if ($sessionCategory != 0) { + $whereConditions['AND s.session_category_id = ?'] = $sessionCategory; + } + + $sessionIds = Database::select( + 's.id', + "$sessionTable s INNER JOIN $innerJoin", + ['where' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ($typeResult === 'count') { + return $sessionIds; + } + + if (!$sessionIds) { + return []; + } + + $sessions = []; + + foreach ($sessionIds as $sessionId) { + $sessions[] = Database::getManager()->find( + 'ChamiloCoreBundle:Session', + $sessionId + ); + } + + return $sessions; + } + + /** + * Search filtered courses by name, and range of price. + * + * @param string $name Optional. The name filter + * @param int $min Optional. The minimun price filter + * @param int $max Optional. The maximum price filter + * + * @return array + */ + private function filterCourseList( + int $start, + int $end, + string $name = null, + int $min = 0, + int $max = 0, + string $typeResult = 'all' + ) { + $itemTable = Database::get_main_table(self::TABLE_ITEM); + $courseTable = Database::get_main_table(TABLE_MAIN_COURSE); + $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE); + + $urlId = api_get_current_access_url_id(); + + $min = floatval($min); + $max = floatval($max); + + $whereConditions = [ + 'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE, + ]; + + if (!empty($name)) { + $whereConditions['AND c.title LIKE %?%'] = $name; + } + + if (!empty($min)) { + $whereConditions['AND i.price >= ?'] = $min; + } + + if (!empty($max)) { + $whereConditions['AND i.price <= ?'] = $max; + } + + $whereConditions['AND url.access_url_id = ?'] = $urlId; + + $courseIds = Database::select( + 'c.id', + "$courseTable c + INNER JOIN $itemTable i + ON c.id = i.product_id + INNER JOIN $urlTable url + ON c.id = url.c_id + ", + ['where' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ($typeResult === 'count') { + return $courseIds; + } + + if (!$courseIds) { + return []; + } + + $courses = []; + foreach ($courseIds as $courseId) { + $courses[] = Database::getManager()->find( + 'ChamiloCoreBundle:Course', + $courseId + ); + } + + return $courses; + } + + /** + * Search filtered sessions by name, and range of price. + * + * @param string $name Optional. The name filter + * @param int $sessionCategory Optional. Session category id + * + * @return array + */ + private function filterSubscriptionSessionList( + int $start, + int $end, + string $name = null, + string $typeResult = 'all', + int $sessionCategory = 0 + ) { + $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); + + $innerJoin = "$subscriptionTable st ON s.id = st.product_id"; + $whereConditions = [ + 'st.product_type = ? ' => self::PRODUCT_TYPE_SESSION, + ]; + + if (!empty($name)) { + $whereConditions['AND s.name LIKE %?%'] = $name; + } + + if ($sessionCategory != 0) { + $whereConditions['AND s.session_category_id = ?'] = $sessionCategory; + } + + $sessionIds = Database::select( + 'DISTINCT s.id', + "$sessionTable s INNER JOIN $innerJoin", + ['where' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ($typeResult === 'count') { + return $sessionIds; + } + + if (!$sessionIds) { + return []; + } + + $sessions = []; + + foreach ($sessionIds as $sessionId) { + $sessions[] = Database::getManager()->find( + 'ChamiloCoreBundle:Session', + $sessionId + ); + } + + return $sessions; + } + + /** + * Search filtered subscriptions courses by name, and range of price. + * + * @param string $name Optional. The name filter + * + * @return array + */ + private function filterSubscriptionCourseList( + int $start, + int $end, + string $name = '', + string $typeResult = 'all' + ) { + $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + $courseTable = Database::get_main_table(TABLE_MAIN_COURSE); + $urlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE); + + $urlId = api_get_current_access_url_id(); + + $whereConditions = [ + 'st.product_type = ? ' => self::PRODUCT_TYPE_COURSE, + ]; + + if (!empty($name)) { + $whereConditions['AND c.title LIKE %?%'] = $name; + } + + $whereConditions['AND url.access_url_id = ?'] = $urlId; + + $courseIds = Database::select( + 'DISTINCT c.id', + "$courseTable c + INNER JOIN $subscriptionTable st + ON c.id = st.product_id + INNER JOIN $urlTable url + ON c.id = url.c_id + ", + ['where' => $whereConditions, 'limit' => "$start, $end"], + $typeResult + ); + + if ($typeResult === 'count') { + return $courseIds; + } + + if (!$courseIds) { + return []; + } + + $courses = []; + foreach ($courseIds as $courseId) { + $courses[] = Database::getManager()->find( + 'ChamiloCoreBundle:Course', + $courseId + ); + } + + return $courses; + } + + /** + * Update the service sale status. + * + * @param int $serviceSaleId The service sale ID + * @param int $newStatus The new status + * + * @return bool + */ + private function updateServiceSaleStatus( + int $serviceSaleId, + int $newStatus = self::SERVICE_STATUS_PENDING + ) { + $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE); + + return Database::update( + $serviceSaleTable, + ['status' => $newStatus], + ['id = ?' => $serviceSaleId] + ); + } + + /** + * Get the items (courses or sessions) of a coupon. + * + * @return array The item data + */ + private function getItemsCoupons(int $couponId, int $productType) + { + $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM); + + if ($productType == self::PRODUCT_TYPE_COURSE) { + $itemTable = Database::get_main_table(TABLE_MAIN_COURSE); + $select = ['ci.product_id as id', 'it.title']; + } elseif ($productType == self::PRODUCT_TYPE_SESSION) { + $itemTable = Database::get_main_table(TABLE_MAIN_SESSION); + $select = ['ci.product_id as id', 'it.name']; + } + + $couponFrom = " + $couponItemTable ci + INNER JOIN $itemTable it + on it.id = ci.product_id and ci.product_type = $productType + "; + + return Database::select( + $select, + $couponFrom, + [ + 'where' => [ + 'ci.coupon_id = ? ' => $couponId, + ], + ] + ); + } + + /** + * Get the services of a coupon. + * + * @param int $couponId The coupon ID + * + * @return array The service data + */ + private function getServicesCoupons(int $couponId) + { + $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE); + $serviceTable = Database::get_main_table(self::TABLE_SERVICES); + + $couponFrom = " + $couponServiceTable cs + INNER JOIN $serviceTable s + on s.id = cs.service_id + "; + + return Database::select( + ['cs.service_id as id', 's.name'], + $couponFrom, + [ + 'where' => [ + 'cs.coupon_id = ? ' => $couponId, + ], + ] + ); + } + + /** + * Get an array of coupons filtered by their status. + * + * @param int $status The coupon activation status + * + * @return array Coupons data + */ + private function getDataCoupons(int $status = null) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + + if ($status != null) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + ' active = ? ' => (int) $status, + ], + 'order' => 'id DESC', + ] + ); + } else { + return Database::select( + ['*'], + $couponTable, + [ + 'order' => 'id DESC', + ] + ); + } + } + + /** + * Get data of a coupon for a product (course or service) by the coupon ID. + * + * @param int $couponId The coupon code code + * @param int $productType The product type + * @param int $productId The product ID + * + * @return array The coupon data + */ + private function getDataCoupon(int $couponId, int $productType = null, int $productId = null) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + + if ($productType == null || $productId == null) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + 'id = ? ' => $couponId, + ], + ], + 'first' + ); + } else { + $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM); + $dtmNow = api_get_utc_datetime(); + + $couponFrom = " + $couponTable c + INNER JOIN $couponItemTable ci + on ci.coupon_id = c.id + "; + + return Database::select( + ['c.*'], + $couponFrom, + [ + 'where' => [ + 'c.id = ? AND ' => $couponId, + 'c.valid_start <= ? AND ' => $dtmNow, + 'c.valid_end >= ? AND ' => $dtmNow, + 'ci.product_type = ? AND ' => $productType, + 'ci.product_id = ?' => $productId, + ], + ], + 'first' + ); + } + } + + /** + * Get data of a coupon for a product (course or service) by the coupon code. + * + * @param string $couponCode The coupon code code + * @param int $productType The product type + * @param int $productId The product ID + * + * @return array The coupon data + */ + private function getDataCouponByCode(string $couponCode, int $productType = null, int $productId = null) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponItemTable = Database::get_main_table(self::TABLE_COUPON_ITEM); + $dtmNow = api_get_utc_datetime(); + + if ($productType == null || $productId == null) { + return Database::select( + ['*'], + $couponTable, + [ + 'where' => [ + 'code = ? ' => $couponCode, + ], + ], + 'first' + ); + } else { + $couponFrom = " + $couponTable c + INNER JOIN $couponItemTable ci + on ci.coupon_id = c.id + "; + + return Database::select( + ['c.*'], + $couponFrom, + [ + 'where' => [ + 'c.code = ? AND ' => $couponCode, + 'c.valid_start <= ? AND ' => $dtmNow, + 'c.valid_end >= ? AND ' => $dtmNow, + 'ci.product_type = ? AND ' => $productType, + 'ci.product_id = ?' => $productId, + ], + ], + 'first' + ); + } + } + + /** + * Get data of a coupon for a service by the coupon ID. + * + * @param int $couponId The coupon ID + * @param int $serviceId The service ID + * + * @return array The coupon data + */ + private function getDataCouponService(int $couponId, int $serviceId) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE); + $dtmNow = api_get_utc_datetime(); + + $couponFrom = " + $couponTable c + INNER JOIN $couponServiceTable cs + on cs.coupon_id = c.id + "; + + return Database::select( + ['c.*'], + $couponFrom, + [ + 'where' => [ + 'c.id = ? AND ' => $couponId, + 'c.valid_start <= ? AND ' => $dtmNow, + 'c.valid_end >= ? AND ' => $dtmNow, + 'cs.service_id = ?' => $serviceId, + ], + ], + 'first' + ); + } + + /** + * Get data of coupon for a service by the coupon code. + * + * @param string $couponCode The coupon code + * @param int $serviceId The service ID + * + * @return array The coupon data + */ + private function getDataCouponServiceByCode(string $couponCode, int $serviceId) + { + $couponTable = Database::get_main_table(self::TABLE_COUPON); + $couponServiceTable = Database::get_main_table(self::TABLE_COUPON_SERVICE); + $dtmNow = api_get_utc_datetime(); + + $couponFrom = " + $couponTable c + INNER JOIN $couponServiceTable cs + on cs.coupon_id = c.id + "; + + return Database::select( + ['c.*'], + $couponFrom, + [ + 'where' => [ + 'c.code = ? AND ' => $couponCode, + 'c.valid_start <= ? AND ' => $dtmNow, + 'c.valid_end >= ? AND ' => $dtmNow, + 'cs.service_id = ?' => $serviceId, + ], + ], + 'first' + ); + } + + /** + * Update a coupon. + * + * @return int + */ + private function updateCoupon(array $coupon) + { + $couponExist = $this->getCouponByCode($coupon['code']); + if (!$couponExist) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return false; + } + + $values = [ + 'valid_start' => $coupon['valid_start'], + 'valid_end' => $coupon['valid_end'], + 'active' => $coupon['active'], + ]; + + return Database::update( + self::TABLE_COUPON, + $values, + ['id = ?' => $coupon['id']] + ); + } + + /** + * Register a coupon. + * + * @return int + */ + private function registerCoupon(array $coupon) + { + $couponExist = $this->getCouponByCode($coupon['code']); + if ($couponExist) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponCodeUsed'), + 'error', + false + ) + ); + + return false; + } + + $values = [ + 'code' => (string) $coupon['code'], + 'discount_type' => (int) $coupon['discount_type'], + 'discount_amount' => $coupon['discount_amount'], + 'valid_start' => $coupon['valid_start'], + 'valid_end' => $coupon['valid_end'], + 'delivered' => 0, + 'active' => $coupon['active'], + ]; + + return Database::insert(self::TABLE_COUPON, $values); + } + + /** + * Register a coupon item. + * + * @param int $couponId The coupon ID + * @param int $productType The product type + * @param int $productId The product ID + * + * @return int + */ + private function registerCouponItem(int $couponId, int $productType, int $productId) + { + $coupon = $this->getDataCoupon($couponId); + if (empty($coupon)) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return false; + } + + $values = [ + 'coupon_id' => $couponId, + 'product_type' => $productType, + 'product_id' => $productId, + ]; + + return Database::insert(self::TABLE_COUPON_ITEM, $values); + } + + /** + * Remove all coupon items for a product type and coupon ID. + * + * @param int $productType The product type + * @param int $couponId The coupon ID + * + * @return int Rows affected. Otherwise return false + */ + private function deleteCouponItemsByCoupon(int $productType, int $couponId) + { + return Database::delete( + Database::get_main_table(self::TABLE_COUPON_ITEM), + [ + 'product_type = ? AND ' => $productType, + 'coupon_id = ?' => $couponId, + ] + ); + } + + /** + * Register a coupon service. + * + * @param int $couponId The coupon ID + * @param int $serviceId The service ID + * + * @return int + */ + private function registerCouponService(int $couponId, int $serviceId) + { + $coupon = $this->getDataCoupon($couponId); + if (empty($coupon)) { + Display::addFlash( + Display::return_message( + $this->get_lang('CouponNoExists'), + 'error', + false + ) + ); + + return false; + } + + $values = [ + 'coupon_id' => $couponId, + 'service_id' => $serviceId, + ]; + + return Database::insert(self::TABLE_COUPON_SERVICE, $values); + } + + /** + * Remove all coupon services for a product type and coupon ID. + * + * @return int Rows affected. Otherwise, return false + */ + private function deleteCouponServicesByCoupon(int $couponId) + { + return Database::delete( + Database::get_main_table(self::TABLE_COUPON_SERVICE), + [ + 'coupon_id = ?' => (int) $couponId, + ] + ); + } + + /** + * Get an array of subscriptions. + * + * @return array Subscriptions data + */ + private function getDataSubscriptions(int $productType, int $productId) + { + $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + + return Database::select( + ['*'], + $subscriptionTable, + [ + 'where' => [ + 'product_type = ? AND ' => (int) $productType, + 'product_id = ? ' => (int) $productId, + ], + 'order' => 'duration ASC', + ] + ); + } + + /** + * Get data of a subscription for a product (course or service) by the subscription ID. + * + * @param int $productType The product type + * @param int $productId The product ID + * @param int $duration The duration (in seconds) + * + * @return array The subscription data + */ + private function getDataSubscription(int $productType, int $productId, int $duration) + { + $subscriptionTable = Database::get_main_table(self::TABLE_SUBSCRIPTION); + + return Database::select( + ['*'], + $subscriptionTable, + [ + 'where' => [ + 'product_type = ? AND ' => $productType, + 'product_id = ? AND ' => $productId, + 'duration = ? ' => $duration, + ], + ], + 'first' + ); + } + + /** + * Update a subscription. + * + * @return int + */ + private function updateSubscription(int $productType, int $productId, int $taxPerc) + { + $values = [ + 'tax_perc' => $taxPerc, + ]; + + return Database::update( + self::TABLE_SUBSCRIPTION, + $values, + [ + 'product_type = ? AND ' => $productType, + 'product_id = ?' => $productId, + ] + ); + + return true; + } + + /** + * Register a subscription. + * + * @return int + */ + private function registerSubscription(array $subscription, array $frequency) + { + $values = [ + 'product_type' => (int) $subscription['product_type'], + 'product_id' => (int) $subscription['product_id'], + 'duration' => (int) $frequency['duration'], + 'currency_id' => (int) $subscription['currency_id'], + 'tax_perc' => (int) $subscription['tax_perc'], + 'price' => (float) $frequency['price'], + ]; + + Database::insert(self::TABLE_SUBSCRIPTION, $values); + + return true; + } + + /** + * Update the subscription sale status. + * + * @param int $saleId The sale ID + * @param int $newStatus The new status + * + * @return bool + */ + private function updateSubscriptionSaleStatus(int $saleId, int $newStatus = self::SALE_STATUS_PENDING) + { + $saleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + + return Database::update( + $saleTable, + ['status' => $newStatus], + ['id = ?' => $saleId] + ); + } + + /** + * Get the user status for the subscription session. + * + * @param int $userId The user ID + * @param Session $session The session + * + * @return string + */ + private function getUserStatusForSubscriptionSession(int $userId, Session $session) + { + if (empty($userId)) { + return 'NO'; + } + + $entityManager = Database::getManager(); + $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser'); + + $buySaleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + + // Check if user bought the course + $sale = Database::select( + 'COUNT(1) as qty', + $buySaleTable, + [ + 'where' => [ + 'user_id = ? AND product_type = ? AND product_id = ? AND status = ? AND (expired is NULL OR expired <> ?)' => [ + $userId, + self::PRODUCT_TYPE_SESSION, + $session->getId(), + self::SALE_STATUS_PENDING, + 1, + ], + ], + ], + 'first' + ); + + if ($sale['qty'] > 0) { + return 'TMP'; + } + + // Check if user is already subscribe to session + $userSubscription = $scuRepo->findBy([ + 'session' => $session, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; + } + + /** + * Get the user status for the subscription course. + * + * @param int $userId The user Id + * @param Course $course The course + * + * @return string + */ + private function getUserStatusForSubscriptionCourse(int $userId, Course $course) + { + if (empty($userId)) { + return 'NO'; + } + + $entityManager = Database::getManager(); + $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser'); + $buySaleTable = Database::get_main_table(self::TABLE_SUBSCRIPTION_SALE); + + // Check if user bought the course + $sale = Database::select( + 'COUNT(1) as qty', + $buySaleTable, + [ + 'where' => [ + 'user_id = ? AND product_type = ? AND product_id = ? AND status = ? AND (expired is NULL OR expired <> ?)' => [ + $userId, + self::PRODUCT_TYPE_COURSE, + $course->getId(), + self::SALE_STATUS_PENDING, + 1, + ], + ], + ], + 'first' + ); + + if ($sale['qty'] > 0) { + return 'TMP'; + } + + // Check if user is already subscribe to course + $userSubscription = $cuRepo->findBy([ + 'course' => $course, + 'user' => $userId, + ]); + + if (!empty($userSubscription)) { + return 'YES'; + } + + return 'NO'; + } +} diff --git a/plugin/buycourses/src/buycourses.ajax.php b/plugin/buycourses/src/buycourses.ajax.php new file mode 100644 index 000000000..1cb8de59f --- /dev/null +++ b/plugin/buycourses/src/buycourses.ajax.php @@ -0,0 +1,612 @@ +get('culqi_enable'); +$action = isset($_GET['a']) ? $_GET['a'] : null; + +$em = Database::getManager(); + +switch ($action) { + case 'verifyPaypal': + if (api_is_anonymous()) { + break; + } + + $userId = isset($_POST['id']) ? (int) $_POST['id'] : ''; + $isUserHavePaypalAccount = $plugin->verifyPaypalAccountByBeneficiary($userId); + if ($isUserHavePaypalAccount) { + echo ''; + } else { + echo '* '.$plugin->get_lang('NoPayPalAccountDetected').''; + } + break; + case 'saleInfo': + if (api_is_anonymous()) { + break; + } + + $saleId = isset($_POST['id']) ? (int) $_POST['id'] : ''; + $sale = $plugin->getSale($saleId); + $productType = $sale['product_type'] == 1 ? get_lang('Course') : get_lang('Session'); + $paymentType = $sale['payment_type'] == 1 ? 'Paypal' : $plugin->get_lang('BankTransfer'); + $productInfo = $sale['product_type'] == 1 + ? api_get_course_info_by_id($sale['product_id']) + : api_get_session_info($sale['product_id']); + $currency = $plugin->getSelectedCurrency(); + if ($sale['product_type'] == 1) { + $productImage = $productInfo['course_image_large']; + } else { + $productImage = ($productInfo['image']) + ? $productInfo['image'] + : Template::get_icon_path('session_default.png'); + } + + $userInfo = api_get_user_info($sale['user_id']); + + $html = '

'.$sale['product_name'].'

'; + $html .= '
'; + $html .= '
'; + $html .= '
    '; + $html .= '
  • '.$plugin->get_lang('OrderPrice').': '.$sale['total_price'].'
  • '; + $html .= '
  • '.$plugin->get_lang('CurrencyType').': '.$currency['iso_code'].'
  • '; + $html .= '
  • '.$plugin->get_lang('ProductType').': '.$productType.'
  • '; + $html .= '
  • '.$plugin->get_lang('OrderDate').': '. + api_format_date( + $sale['date'], + DATE_TIME_FORMAT_LONG_24H + ).'
  • '; + $html .= '
  • '.$plugin->get_lang('Buyer').': '.$userInfo['complete_name'].'
  • '; + $html .= '
  • '.$plugin->get_lang('PaymentMethods').': '.$paymentType.'
  • '; + $html .= '
'; + $html .= '
'; + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= '
'; + + echo $html; + break; + case 'stats': + if (api_is_anonymous()) { + break; + } + + $stats = []; + $stats['completed_count'] = 0; + $stats['completed_total_amount'] = 0; + $stats['pending_count'] = 0; + $stats['pending_total_amount'] = 0; + $stats['canceled_count'] = 0; + $stats['canceled_total_amount'] = 0; + + $completedPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED); + $pendingPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_PENDING); + $canceledPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_CANCELED); + $currency = $plugin->getSelectedCurrency(); + + foreach ($completedPayouts as $completed) { + $stats['completed_count'] = count($completedPayouts); + $stats['completed_total_amount'] += $completed['commission']; + $stats['completed_total_amount'] = number_format($stats['completed_total_amount'], 2); + } + + foreach ($pendingPayouts as $pending) { + $stats['pending_count'] = count($pendingPayouts); + $stats['pending_total_amount'] += $pending['commission']; + $stats['pending_total_amount'] = number_format($stats['pending_total_amount'], 2); + } + + foreach ($canceledPayouts as $canceled) { + $stats['canceled_count'] = count($canceledPayouts); + $stats['canceled_total_amount'] += $canceled['commission']; + $stats['canceled_total_amount'] = number_format($stats['canceled_total_amount'], 2); + } + + $html = '
' + .'

' + .'

    ' + .'
  • '.get_plugin_lang("PayoutsTotalCompleted", "BuyCoursesPlugin").' '.$stats['completed_count'] + .' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['completed_total_amount'].' ' + .$currency['iso_code'].'
  • ' + .'
  • '.get_plugin_lang("PayoutsTotalPending", "BuyCoursesPlugin").' '.$stats['pending_count'].' - ' + .get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['pending_total_amount'].' ' + .$currency['iso_code'].'
  • ' + .'
  • '.get_plugin_lang("PayoutsTotalCanceled", "BuyCoursesPlugin").' '.$stats['canceled_count'] + .' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' '.$stats['canceled_total_amount'].' ' + .$currency['iso_code'].'
  • ' + .'
' + .'

'; + $html .= '
'; + echo $html; + break; + case 'processPayout': + if (api_is_anonymous()) { + break; + } + + $html = ''; + $allPays = []; + $totalAccounts = 0; + $totalPayout = 0; + + $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; + + if (!$payouts) { + echo Display::return_message( + get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), + 'error', + false + ); + break; + } + + foreach ($payouts as $index => $id) { + $allPays[] = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_PENDING, $id); + } + + foreach ($allPays as $payout) { + $totalPayout += number_format($payout['commission'], 2); + $totalAccounts++; + } + + $currentCurrency = $plugin->getSelectedCurrency(); + $isoCode = $currentCurrency['iso_code']; + $html .= '

'.get_plugin_lang("VerifyTotalAmountToProceedPayout", "BuyCoursesPlugin").'

'; + $html .= '' + .'

' + .'

    ' + .'
  • '.get_plugin_lang("TotalAcounts", "BuyCoursesPlugin").' '.$totalAccounts.'
  • ' + .'
  • '.get_plugin_lang("TotalPayout", "BuyCoursesPlugin").' '.$isoCode.' '.$totalPayout.'
  • ' + .'
' + .'

'; + $html .= '

'.get_plugin_lang("CautionThisProcessCantBeCanceled", "BuyCoursesPlugin").'

'; + $html .= '

'; + $html .= '
'; + + echo $html; + break; + case 'proceedPayout': + if (api_is_anonymous()) { + break; + } + + $paypalParams = $plugin->getPaypalParams(); + + $pruebas = $paypalParams['sandbox'] == 1; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + + require_once "paypalfunctions.php"; + + $allPayouts = []; + $totalAccounts = 0; + $totalPayout = 0; + + $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; + + if (!$payouts) { + echo Display::return_message( + get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), + 'error', + false + ); + break; + } + + foreach ($payouts as $index => $id) { + $allPayouts[] = $plugin->getPayouts( + BuyCoursesPlugin::PAYOUT_STATUS_PENDING, + $id + ); + } + + $currentCurrency = $plugin->getSelectedCurrency(); + $isoCode = $currentCurrency['iso_code']; + $result = MassPayment($allPayouts, $isoCode); + if ($result['ACK'] === 'Success') { + foreach ($allPayouts as $payout) { + $plugin->setStatusPayouts( + $payout['id'], + BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED + ); + if ($plugin->get('invoicing_enable') === 'true') { + $plugin->setInvoice($payout['id']); + } + } + + echo Display::return_message( + get_plugin_lang("PayoutSuccess", "BuyCoursesPlugin"), + 'success', + false + ); + } else { + echo Display::return_message( + ''.$result['L_SEVERITYCODE0'].' '.$result['L_ERRORCODE0'].' - '.$result['L_SHORTMESSAGE0'] + .'
  • '.$result['L_LONGMESSAGE0'].'
', + 'error', + false + ); + } + break; + case 'cancelPayout': + if (api_is_anonymous()) { + break; + } + + // $payoutId only gets used in setStatusPayout(), where it is filtered + $payoutId = isset($_POST['id']) ? $_POST['id'] : ''; + $plugin->setStatusPayouts( + $payoutId, + BuyCoursesPlugin::PAYOUT_STATUS_CANCELED + ); + + echo ''; + + break; + case 'culqi_cargo': + if (!$culqiEnable) { + break; + } + + $tokenId = $_REQUEST['token_id']; + $saleId = $_REQUEST['sale_id']; + + if (!$tokenId || !$saleId) { + break; + } + $sale = $plugin->getSale($saleId); + if (!$sale) { + break; + } + + require_once 'Requests.php'; + Requests::register_autoloader(); + require_once 'culqi.php'; + + $culqiParams = $plugin->getCulqiParams(); + + // API Key y autenticación + $SECRET_API_KEY = $culqiParams['api_key']; + $culqi = new Culqi\Culqi(['api_key' => $SECRET_API_KEY]); + + $environment = $culqiParams['integration']; + $environment = $environment + ? BuyCoursesPlugin::CULQI_INTEGRATION_TYPE + : BuyCoursesPlugin::CULQI_PRODUCTION_TYPE; + + $culqi->setEnv($environment); + + $user = api_get_user_info(); + $currency = $plugin->getSelectedCurrency(); + + try { + $cargo = $culqi->Cargos->create([ + "moneda" => $currency['iso_code'], + "monto" => intval(floatval($sale['price']) * 100), + "usuario" => $user['username'], + "descripcion" => $sale['product_name'], + "pedido" => $sale['reference'], + "codigo_pais" => "PE", + "direccion" => get_lang('None'), + "ciudad" => get_lang('None'), + "telefono" => 0, + "nombres" => $user['firstname'], + "apellidos" => $user['lastname'], + "correo_electronico" => $user['email'], + "token" => $tokenId, + ]); + + if (is_object($cargo)) { + $saleIsCompleted = $plugin->completeSale($sale['id']); + + if ($saleIsCompleted) { + Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) + ); + } + } + + echo json_encode($cargo); + } catch (Exception $e) { + $cargo = json_decode($e->getMessage(), true); + $plugin->cancelSale($sale['id']); + unset($_SESSION['bc_sale_id']); + if (is_array($cargo)) { + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('ErrorOccurred'), $cargo['codigo'], $cargo['mensaje']), + 'error', + false + ) + ); + } else { + Display::addFlash( + Display::return_message( + $plugin->get_lang('ErrorContactPlatformAdmin'), + 'error', + false + ) + ); + } + } + break; + case 'culqi_cargo_service': + if (!$culqiEnable) { + break; + } + + $tokenId = $_REQUEST['token_id']; + $serviceSaleId = $_REQUEST['service_sale_id']; + + if (!$tokenId || !$serviceSaleId) { + break; + } + + $serviceSale = $plugin->getServiceSale($serviceSaleId); + + if (!$serviceSale) { + break; + } + + require_once 'Requests.php'; + Requests::register_autoloader(); + require_once 'culqi.php'; + $culqiParams = $plugin->getCulqiParams(); + + // API Key y autenticación + $SECRET_API_KEY = $culqiParams['api_key']; + $culqi = new Culqi\Culqi(['api_key' => $SECRET_API_KEY]); + + $environment = $culqiParams['integration']; + $environment = $environment + ? BuyCoursesPlugin::CULQI_INTEGRATION_TYPE + : BuyCoursesPlugin::CULQI_PRODUCTION_TYPE; + + $culqi->setEnv($environment); + $user = api_get_user_info(); + + try { + $cargo = $culqi->Cargos->create([ + "moneda" => $serviceSale['currency'], + "monto" => intval(floatval($serviceSale['price']) * 100), + "usuario" => $user['username'], + "descripcion" => $serviceSale['service']['name'], + "pedido" => $serviceSale['reference'], + "codigo_pais" => "PE", + "direccion" => get_lang('None'), + "ciudad" => get_lang('None'), + "telefono" => 0, + "nombres" => $user['firstname'], + "apellidos" => $user['lastname'], + "correo_electronico" => $user['email'], + "token" => $tokenId, + ]); + + if (is_object($cargo)) { + $saleIsCompleted = $plugin->completeServiceSale($serviceSale['id']); + if ($saleIsCompleted) { + Display::addFlash( + Display::return_message( + sprintf( + $plugin->get_lang('SubscriptionToCourseXSuccessful'), + $serviceSale['service']['name'] + ), + 'success' + ) + ); + } + } + + echo json_encode($cargo); + } catch (Exception $e) { + $cargo = json_decode($e->getMessage(), true); + $plugin->cancelServiceSale($serviceSale['id']); + + unset($_SESSION['bc_sale_id']); + + if (is_array($cargo)) { + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('ErrorOccurred'), $cargo['codigo'], $cargo['mensaje']), + 'error', + false + ) + ); + } else { + Display::addFlash( + Display::return_message( + $plugin->get_lang('ErrorContactPlatformAdmin'), + 'error', + false + ) + ); + } + } + break; + case 'service_sale_info': + $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; + $serviceSale = $plugin->getServiceSale($id); + $isAdmin = api_is_platform_admin(); + if (!$serviceSale) { + break; + } + + $ajaxCallFile = $plugin->getPath('SRC').'buycourses.ajax.php'; + $serviceImg = $plugin->getPath('SERVICE_IMAGES').$serviceSale['service']['image']; + $html = ""; + $html .= "
"; + $html .= "{$plugin->get_lang('ServiceInformation')}"; + $html .= "
    "; + $html .= "
  • {$plugin->get_lang('ServiceName')}: {$serviceSale['service']['name']}
  • "; + $html .= "
  • {$plugin->get_lang('Description')}: {$serviceSale['service']['description']}
  • "; + $nodeType = $serviceSale['node_type']; + $nodeName = ''; + if ($nodeType == BuyCoursesPlugin::SERVICE_TYPE_USER) { + $nodeType = get_lang('User'); + /** @var User $user */ + $user = UserManager::getManager()->find($serviceSale['node_id']); + $nodeName = $user ? $user->getCompleteNameWithUsername() : null; + } else { + if ($nodeType == BuyCoursesPlugin::SERVICE_TYPE_COURSE) { + $nodeType = get_lang('Course'); + /** @var Course $course */ + $course = $em->find('ChamiloCoreBundle:Course', $serviceSale['node_id']); + $nodeName = $course ? $course->getTitle() : null; + } else { + if ($nodeType == BuyCoursesPlugin::SERVICE_TYPE_SESSION) { + $nodeType = get_lang('Session'); + /** @var Session $session */ + $session = $em->find('ChamiloCoreBundle:Session', $serviceSale['node_id']); + $nodeName = $session ? $session->getName() : null; + } else { + if ($nodeType == BuyCoursesPlugin::SERVICE_TYPE_LP_FINAL_ITEM) { + $nodeType = get_lang('TemplateTitleCertificate'); + /** @var CLp $lp */ + $lp = $em->find('ChamiloCourseBundle:CLp', $serviceSale['node_id']); + $nodeName = $lp ? $lp->getName() : null; + } + } + } + } + + $html .= "
"; + $html .= "{$plugin->get_lang('SaleInfo')}"; + $html .= "
    "; + $html .= "
  • {$plugin->get_lang('BoughtBy')}: {$serviceSale['buyer']['name']}
  • "; + $html .= "
  • {$plugin->get_lang('PurchaserUser')}: {$serviceSale['buyer']['username']}
  • "; + $html .= "
  • {$plugin->get_lang('Total')}: {$serviceSale['service']['total_price']}
  • "; + $orderDate = api_format_date($serviceSale['buy_date'], DATE_FORMAT_LONG); + $html .= "
  • {$plugin->get_lang('OrderDate')}: $orderDate
  • "; + $paymentType = $serviceSale['payment_type']; + if ($paymentType == BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL) { + $paymentType = 'PayPal'; + } else { + if ($paymentType == BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER) { + $paymentType = $plugin->get_lang('BankTransfer'); + } else { + if ($paymentType == BuyCoursesPlugin::PAYMENT_TYPE_CULQI) { + $paymentType = 'Culqi'; + } + } + } + $html .= "
  • {$plugin->get_lang('PaymentMethod')}: $paymentType
  • "; + $status = $serviceSale['status']; + $buttons = ''; + if ($status == BuyCoursesPlugin::SERVICE_STATUS_COMPLETED) { + $status = $plugin->get_lang('Active'); + } else { + if ($status == BuyCoursesPlugin::SERVICE_STATUS_PENDING) { + $status = $plugin->get_lang('Pending'); + if ($isAdmin) { + $buttons .= "{$plugin->get_lang('ConfirmOrder')}"; + $buttons .= "{$plugin->get_lang('CancelOrder')}"; + } + } else { + if ($status == BuyCoursesPlugin::SERVICE_STATUS_CANCELLED) { + $status = $plugin->get_lang('Cancelled'); + } + } + } + $html .= "
  • {$plugin->get_lang('Status')}: $status
  • "; + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= $buttons; + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= ""; + + echo $html; + break; + case 'service_sale_confirm': + $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; + $serviceSale = $plugin->getServiceSale($id); + $response = $plugin->completeServiceSale($id); + $html = "
"; + + if ($response) { + $html .= Display::return_message( + sprintf($plugin->get_lang('SubscriptionToServiceXSuccessful'), $serviceSale['service']['name']), + 'success' + ); + } else { + $html .= Display::return_message('Error - '.$plugin->get_lang('ErrorContactPlatformAdmin'), 'error'); + } + + $html .= "".$plugin->get_lang('ClickHereToFinish').""; + $html .= "
"; + $html .= ""; + echo $html; + break; + case 'service_sale_cancel': + $id = isset($_POST['id']) ? (int) $_POST['id'] : 0; + $response = $plugin->cancelServiceSale($id); + $html = ''; + $html .= "
"; + + if ($response) { + $html .= Display::return_message( + $plugin->get_lang('OrderCancelled'), + 'warning' + ); + } else { + $html .= Display::return_message('Error - '.$plugin->get_lang('ErrorContactPlatformAdmin'), 'error'); + } + + $html .= "".$plugin->get_lang('ClickHereToFinish').""; + $html .= "
"; + $html .= ""; + echo $html; + break; +} diff --git a/plugin/buycourses/src/cecabank_cancel.php b/plugin/buycourses/src/cecabank_cancel.php new file mode 100644 index 000000000..174faad75 --- /dev/null +++ b/plugin/buycourses/src/cecabank_cancel.php @@ -0,0 +1,30 @@ +get('cecabank_enable') === 'true'; + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/src/cecabank_response.php b/plugin/buycourses/src/cecabank_response.php new file mode 100644 index 000000000..1d6398d48 --- /dev/null +++ b/plugin/buycourses/src/cecabank_response.php @@ -0,0 +1,53 @@ +get('cecabank_enable') === 'true'; + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$receivedAmount = (float) $_POST['Importe']; + +if (empty($_POST['Num_operacion']) || empty($_POST['Firma']) || empty($receivedAmount)) { + api_not_allowed(true); +} + +$signature = $plugin->getCecabankSignature($_POST['Num_operacion'], $receivedAmount); + +if ($signature != $_POST['Firma']) { + api_not_allowed(true); +} + +$sale = $plugin->getSaleFromReference($_POST['Num_operacion']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$buyingCourse = false; +$buyingSession = false; + +switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + break; +} + +$saleIsCompleted = $plugin->completeSale($sale['id']); +if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); +} diff --git a/plugin/buycourses/src/cecabank_success.php b/plugin/buycourses/src/cecabank_success.php new file mode 100644 index 000000000..9b95b375e --- /dev/null +++ b/plugin/buycourses/src/cecabank_success.php @@ -0,0 +1,30 @@ +get('cecabank_enable') === 'true'; + +if (!$cecabankEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/src/configure_coupon.php b/plugin/buycourses/src/configure_coupon.php new file mode 100644 index 000000000..2db7a33f1 --- /dev/null +++ b/plugin/buycourses/src/configure_coupon.php @@ -0,0 +1,204 @@ +getCouponInfo($couponId); + +if (!isset($coupon)) { + api_not_allowed(); +} + +$couponDateRangeFrom = $coupon['valid_start']; +$couponDateRangeTo = $coupon['valid_end']; + +$includeSession = $plugin->get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +$coursesList = CourseManager::get_courses_list( + 0, + 0, + 'title', + 'asc', + -1, + null, + api_get_current_access_url_id(), + false, + [], + [] +); + +foreach ($coursesList as $course) { + $courses[$course['id']] = $course['title']; +} + +$sessionsList = SessionManager::get_sessions_list( + [], + [], + null, + null, + api_get_current_access_url_id(), + [] +); + +foreach ($sessionsList as $session) { + $sessions[$session['id']] = $session['name']; +} + +$servicesList = $plugin->getAllServices(); + +foreach ($servicesList as $service) { + $services[$service['id']] = $service['name']; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); + +// Build the form +$form = new FormValidator('add_coupon'); +$form->addText('code', $plugin->get_lang('CouponCode'), false); +$form->addText('discount_type', $plugin->get_lang('CouponDiscountType'), false); +$form->addText('discount_amount', $plugin->get_lang('CouponDiscount'), false); +$form->addDateRangePicker( + 'date', + get_lang('Date'), + true, + [ + 'value' => "$couponDateRangeFrom / $couponDateRangeTo", + ] +); + +$form->addCheckBox('active', $plugin->get_lang('CouponActive')); +$form->addElement( + 'advmultiselect', + 'courses', + get_lang('Courses'), + $courses +); + +if ($includeSession) { + $form->addElement( + 'advmultiselect', + 'sessions', + get_lang('Sessions'), + $sessions + ); +} + +if ($includeServices) { + $form->addElement( + 'advmultiselect', + 'services', + get_lang('Services'), + $services + ); +} + +$form->addHidden('id', null); + +$coursesAdded = $coupon["courses"]; +if (!empty($coursesAdded)) { + $coursesAdded = array_column($coursesAdded, 'id'); +} + +$sessionsAdded = $coupon["sessions"]; +if (!empty($sessionsAdded)) { + $sessionsAdded = array_column($sessionsAdded, 'id'); +} + +$servicesAdded = $coupon["services"]; +if (!empty($servicesAdded)) { + $servicesAdded = array_column($servicesAdded, 'id'); +} + +$formDefaults = [ + 'id' => $coupon['id'], + 'code' => $coupon['code'], + 'discount_type' => $discountTypes[$coupon['discount_type']], + 'discount_amount' => $coupon['discount_amount'], + 'date' => "$couponDateRangeFrom / $couponDateRangeTo", + 'active' => $coupon['active'], + 'courses' => $coursesAdded, + 'sessions' => $sessionsAdded, + 'services' => $servicesAdded, +]; + +$button = $form->addButtonSave(get_lang('Save')); +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +$form->freeze(['code', 'discount_type', 'discount_amount']); + +if ($form->validate()) { + $formValues = $form->exportValues(); + + $coupon['id'] = $formValues['id']; + $coupon['valid_start'] = $formValues['date_start']; + $coupon['valid_end'] = $formValues['date_end']; + $coupon['active'] = $formValues['active']; + $coupon['courses'] = isset($formValues['courses']) ? $formValues['courses'] : []; + $coupon['sessions'] = isset($formValues['sessions']) ? $formValues['sessions'] : []; + $coupon['services'] = isset($formValues['services']) ? $formValues['services'] : []; + + $result = $plugin->updateCouponData($coupon); + + if ($result) { + Display::addFlash( + Display::return_message( + $plugin->get_lang('CouponUpdate'), + 'success', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/configure_coupon.php?id='.$coupon["id"]); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('ConfigureCoupon'); +$interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'coupons.php', + 'name' => $plugin->get_lang('CouponList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/configure_course.php b/plugin/buycourses/src/configure_course.php new file mode 100644 index 000000000..94aed74ed --- /dev/null +++ b/plugin/buycourses/src/configure_course.php @@ -0,0 +1,363 @@ +get('commissions_enable'); + +if ($commissionsEnable == 'true') { + $htmlHeadXtra[] = ''; + $commissions = ''; +} + +$includeSession = $plugin->get('include_sessions') === 'true'; +$editingCourse = $type === BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$editingSession = $type === BuyCoursesPlugin::PRODUCT_TYPE_SESSION; + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); + $currency = null; +} + +$currencyIso = null; + +if ($editingCourse) { + $course = $entityManager->find('ChamiloCoreBundle:Course', $id); + + if (!$course) { + api_not_allowed(true); + } + + /*if (!$plugin->isValidCourse($course)) { + api_not_allowed(true); + }*/ + + $courseItem = $plugin->getCourseForConfiguration($course, $currency); + $defaultBeneficiaries = []; + $teachers = $course->getTeachers(); + $teachersOptions = []; + + foreach ($teachers as $courseTeacher) { + $teacher = $courseTeacher->getUser(); + $teachersOptions[] = [ + 'text' => $teacher->getCompleteName(), + 'value' => $teacher->getId(), + ]; + $defaultBeneficiaries[] = $teacher->getId(); + } + + if (!empty($courseItem['item_id'])) { + $currentBeneficiaries = $plugin->getItemBeneficiaries($courseItem['course_id']); + if (!empty($currentBeneficiaries)) { + $defaultBeneficiaries = array_column($currentBeneficiaries, 'user_id'); + if ($commissionsEnable === 'true') { + $defaultCommissions = array_column($currentBeneficiaries, 'commissions'); + foreach ($defaultCommissions as $defaultCommission) { + $commissions .= $defaultCommission.','; + } + $commissions = substr($commissions, 0, -1); + } + } + + $currencyIso = $courseItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => $courseItem['visible'], + 'price' => $courseItem['price'], + 'tax_perc' => $courseItem['tax_perc'], + 'beneficiaries' => $defaultBeneficiaries, + $commissionsEnable == 'true' ? 'commissions' : '' => $commissionsEnable == 'true' ? $commissions : '', + ]; + } else { + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => false, + 'price' => 0, + 'tax_perc' => 0, + 'beneficiaries' => [], + $commissionsEnable == 'true' ? 'commissions' : '' => $commissionsEnable == 'true' ? '' : '', + ]; + } +} elseif ($editingSession) { + if (!$includeSession) { + api_not_allowed(true); + } + + $session = $entityManager->find('ChamiloCoreBundle:Session', $id); + if (!$session) { + api_not_allowed(true); + } + + $sessionItem = $plugin->getSessionForConfiguration($session, $currency); + $generalCoach = $session->getGeneralCoach(); + $generalCoachOption = [ + 'text' => $generalCoach->getCompleteName(), + 'value' => $generalCoach->getId(), + ]; + $defaultBeneficiaries = [ + $generalCoach->getId(), + ]; + $courseCoachesOptions = []; + $sessionCourses = $session->getCourses(); + + foreach ($sessionCourses as $sessionCourse) { + $courseCoaches = $userRepo->getCoachesForSessionCourse($session, $sessionCourse->getCourse()); + + foreach ($courseCoaches as $courseCoach) { + if ($generalCoach->getId() === $courseCoach->getId()) { + continue; + } + + $courseCoachesOptions[] = [ + 'text' => $courseCoach->getCompleteName(), + 'value' => $courseCoach->getId(), + ]; + $defaultBeneficiaries[] = $courseCoach->getId(); + } + } + + if ($sessionItem['item_id']) { + $currentBeneficiaries = $plugin->getItemBeneficiaries($sessionItem['item_id']); + } + + if (!empty($currentBeneficiaries)) { + $defaultBeneficiaries = array_column($currentBeneficiaries, 'user_id'); + + if ($commissionsEnable == 'true') { + $defaultCommissions = array_column($currentBeneficiaries, 'commissions'); + + foreach ($defaultCommissions as $defaultCommission) { + $commissions .= $defaultCommission.','; + } + + $commissions = substr($commissions, 0, -1); + } + } + + $currencyIso = $sessionItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Session'), + 'id' => $session->getId(), + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION, + 'name' => $sessionItem['session_name'], + 'visible' => $sessionItem['visible'], + 'price' => $sessionItem['price'], + 'tax_perc' => $sessionItem['tax_perc'], + 'beneficiaries' => $defaultBeneficiaries, + $commissionsEnable == 'true' ? 'commissions' : '' => $commissionsEnable == 'true' ? $commissions : '', + ]; +} else { + api_not_allowed(true); +} + +if ($commissionsEnable === 'true') { + $htmlHeadXtra[] = " + + "; +} + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('beneficiaries'); +$form->addText('product_type', $plugin->get_lang('ProductType'), false); +$form->addText('name', get_lang('Name'), false); +$form->addCheckBox( + 'visible', + $plugin->get_lang('VisibleInCatalog'), + $plugin->get_lang('ShowOnCourseCatalog') +); +$form->addElement( + 'number', + 'price', + [$plugin->get_lang('Price'), null, $currencyIso], + ['step' => 0.01] +); +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); +$beneficiariesSelect = $form->addSelect( + 'beneficiaries', + $plugin->get_lang('Beneficiaries'), + null, + ['multiple' => 'multiple', 'id' => 'selectBox'] +); + +if ($editingCourse) { + $teachersOptions = api_unique_multidim_array($teachersOptions, 'value'); + $beneficiariesSelect->addOptGroup($teachersOptions, get_lang('Teachers')); +} elseif ($editingSession) { + $courseCoachesOptions = api_unique_multidim_array($courseCoachesOptions, 'value'); + $beneficiariesSelect->addOptGroup([$generalCoachOption], get_lang('SessionGeneralCoach')); + $beneficiariesSelect->addOptGroup($courseCoachesOptions, get_lang('SessionCourseCoach')); +} + +if ($commissionsEnable === 'true') { + $platformCommission = $plugin->getPlatformCommission(); + $form->addHtml( + ' +
+ +
+ '.Display::return_message( + sprintf($plugin->get_lang('TheActualPlatformCommissionIsX'), $platformCommission['commission'].'%'), + 'info', + false + ).' +
+
+
' + ); + $form->addHidden('commissions', ''); +} + +$form->addHidden('type', null); +$form->addHidden('id', null); +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +$form->freeze(['product_type', 'name']); + +if ($form->validate()) { + $formValues = $form->exportValues(); + $id = $formValues['id']; + $type = $formValues['type']; + + $productItem = $plugin->getItemByProduct($id, $type); + if (isset($formValues['visible'])) { + $taxPerc = $formValues['tax_perc'] != '' ? (int) $formValues['tax_perc'] : null; + if (!empty($productItem)) { + $plugin->updateItem( + [ + 'price' => floatval($formValues['price']), + 'tax_perc' => $taxPerc, + ], + $id, + $type + ); + } else { + $itemId = $plugin->registerItem([ + 'currency_id' => (int) $currency['id'], + 'product_type' => $type, + 'product_id' => $id, + 'price' => floatval($_POST['price']), + 'tax_perc' => $taxPerc, + ]); + $productItem['id'] = $itemId; + } + + $plugin->deleteItemBeneficiaries($productItem['id']); + + if (isset($formValues['beneficiaries'])) { + if ($commissionsEnable === 'true') { + $usersId = $formValues['beneficiaries']; + $commissions = explode(',', $formValues['commissions']); + $commissions = (count($usersId) != count($commissions)) + ? array_fill(0, count($usersId), 0) + : $commissions; + $beneficiaries = array_combine($usersId, $commissions); + } else { + $usersId = $formValues['beneficiaries']; + $commissions = array_fill(0, count($usersId), 0); + $beneficiaries = array_combine($usersId, $commissions); + } + $plugin->registerItemBeneficiaries($productItem['id'], $beneficiaries); + } + } else { + if (!empty($productItem['id'])) { + $plugin->deleteItem($productItem['id']); + } + } + $url = 'list.php'; + if ($type == 2) { + $url = 'list_session.php'; + } + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/'.$url); + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = ''; +$interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => get_lang('Configuration'), +]; +switch ($type) { + case 2: + $interbreadcrumb[] = [ + 'url' => 'list_session.php', + 'name' => $plugin->get_lang('Sessions'), + ]; + $templateName = $plugin->get_lang('Sessions'); + break; + default: + $interbreadcrumb[] = [ + 'url' => 'list.php', + 'name' => $plugin->get_lang('AvailableCourses'), + ]; + $templateName = $plugin->get_lang('AvailableCourse'); +} + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/configure_frequency.php b/plugin/buycourses/src/configure_frequency.php new file mode 100644 index 000000000..814ceeec0 --- /dev/null +++ b/plugin/buycourses/src/configure_frequency.php @@ -0,0 +1,119 @@ +selectFrequency($_GET['d']); + + if (!empty($frequency)) { + $subscriptionsItems = $plugin->getSubscriptionsItemsByDuration($_GET['d']); + + if (empty($subscriptionsItems)) { + $plugin->deleteFrequency($_GET['d']); + + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyRemoved'), 'success') + ); + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionPeriodOnUse'), 'error') + ); + } + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotExits'), 'error') + ); + } + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyIncorrect'), 'error') + ); + } + + header('Location: '.api_get_self()); + exit; + } +} + +$frequencies = $plugin->getFrequenciesList(); + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_frequency'); + +$form->addText('name', get_lang('Name'), false); + +$form->addElement( + 'number', + 'duration', + [$plugin->get_lang('Duration'), $plugin->get_lang('Days')], + ['step' => 1, 'placeholder' => $plugin->get_lang('SubscriptionFrequencyValueDays')] +); + +$button = $form->addButtonSave(get_lang('Save')); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $duration = $formValues['duration']; + $name = $formValues['name']; + + $frequency = $plugin->selectFrequency($duration); + + if (!empty($frequency)) { + $result = $plugin->updateFrequency($duration, $name); + + if (!isset($result)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotUpdated'), 'error') + ); + } + } else { + $result = $plugin->addFrequency($duration, $name); + + if (!isset($result)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyNotSaved'), 'error') + ); + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/configure_frequency.php'); + + exit; +} + +//$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('FrequencyAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('frequencies_list', $frequencies); + +$content = $template->fetch('buycourses/view/configure_frequency.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/configure_subscription.php b/plugin/buycourses/src/configure_subscription.php new file mode 100644 index 000000000..8a438021b --- /dev/null +++ b/plugin/buycourses/src/configure_subscription.php @@ -0,0 +1,246 @@ +get('include_sessions') === 'true'; + +if (isset($_GET['action'], $_GET['d'])) { + if ($_GET['action'] == 'delete_frequency') { + $plugin->deleteSubscription($type, $id, $_GET['d']); + + Display::addFlash( + Display::return_message(get_lang('ItemRemoved'), 'success') + ); + + header('Location: '.api_get_self().'?'.$queryString); + exit; + } +} + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$subscriptions = $plugin->getSubscriptions($type, $id); + +$taxtPerc = 0; + +if (isset($subscriptions) && !empty($subscriptions)) { + $taxtPerc = $subscriptions[0]['tax_perc']; +} + +$currencyIso = null; + +if ($editingCourse) { + $course = $entityManager->find('ChamiloCoreBundle:Course', $id); + if (!$course) { + api_not_allowed(true); + } + + $courseItem = $plugin->getCourseForConfiguration($course, $currency); + + $currencyIso = $courseItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => $courseItem['visible'], + 'tax_perc' => $taxtPerc, + ]; +} elseif ($editingSession) { + if (!$includeSession) { + api_not_allowed(true); + } + + $session = $entityManager->find('ChamiloCoreBundle:Session', $id); + if (!$session) { + api_not_allowed(true); + } + + $sessionItem = $plugin->getSessionForConfiguration($session, $currency); + + $currencyIso = $sessionItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Session'), + 'id' => $session->getId(), + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION, + 'name' => $sessionItem['session_name'], + 'visible' => $sessionItem['visible'], + 'tax_perc' => $taxtPerc, + ]; +} else { + api_not_allowed(true); +} + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_subscription'); + +$form->addText('product_type', $plugin->get_lang('ProductType'), false); +$form->addText('name', get_lang('Name'), false); + +$form->freeze(['product_type', 'name']); + +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); + +$frequenciesOptions = $plugin->getFrequencies(); + +$frequencyForm = new FormValidator('frequency_config', 'post', api_get_self().'?'.$queryString); + +$frequencyFormDefaults = [ + 'id' => $id, + 'type' => $type, + 'tax_perc' => $taxtPerc, + 'currency_id' => $currency['id'], +]; + +$frequencyForm->setDefaults($frequencyFormDefaults); + +if ($frequencyForm->validate()) { + $frequencyFormValues = $frequencyForm->getSubmitValues(); + + $subscription['product_id'] = $frequencyFormValues['id']; + $subscription['product_type'] = $frequencyFormValues['type']; + $subscription['tax_perc'] = $frequencyFormValues['tax_perc'] != '' ? (int) $frequencyFormValues['tax_perc'] : null; + $subscription['currency_id'] = $currency['id']; + $duration = $frequencyFormValues['duration']; + $price = $frequencyFormValues['price']; + + for ($i = 0; $i <= count($subscriptions); $i++) { + if ($subscriptions[$i]['duration'] == $duration) { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionAlreadyExists'), 'error') + ); + + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + } + + $subscription['frequencies'] = [['duration' => $duration, 'price' => $price]]; + + $result = $plugin->addNewSubscription($subscription); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self().'?'.$queryString); + exit; +} + +$frequencyForm->addElement( + 'select', + 'duration', + $plugin->get_lang('Duration'), + $frequenciesOptions, + ['cols-size' => [2, 8, 2]] +); + +$frequencyForm->addElement( + 'number', + 'price', + [$plugin->get_lang('Price'), null, $currencyIso], + false, + [ + 'step' => 1, + 'cols-size' => [3, 8, 1], + ] +); + +$frequencyForm->addHidden('type', $type); +$frequencyForm->addHidden('id', $id); +$frequencyForm->addHidden('tax_perc', $taxtPerc); +$frequencyForm->addHidden('currency_id', $currency['id']); +$frequencyForm->addButtonCreate('Add'); + +for ($i = 0; $i < count($subscriptions); $i++) { + if ($subscriptions[$i]['duration'] > 0) { + $subscriptions[$i]['durationName'] = $frequenciesOptions[$subscriptions[$i]['duration']]; + } +} + +$form->addHidden('type', $type); +$form->addHidden('id', $id); +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $id = $formValues['id']; + $type = $formValues['type']; + $taxPerc = $formValues['tax_perc'] != '' ? (int) $formValues['tax_perc'] : null; + + $result = $plugin->updateSubscriptions($type, $id, $taxPerc); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscriptions_courses.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('SubscriptionAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('frequency_form', $frequencyForm->returnForm()); +$template->assign('subscriptions', $subscriptions); +$template->assign('currencyIso', $currencyIso); + +$content = $template->fetch('buycourses/view/configure_subscription.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/coupon_add.php b/plugin/buycourses/src/coupon_add.php new file mode 100644 index 000000000..8ef7e0b5d --- /dev/null +++ b/plugin/buycourses/src/coupon_add.php @@ -0,0 +1,167 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +$coursesList = CourseManager::get_courses_list( + 0, + 0, + 'title', + 'asc', + -1, + null, + api_get_current_access_url_id(), + false, + [], + [] +); + +foreach ($coursesList as $course) { + $courses[$course['id']] = $course['title']; +} + +$sessionsList = SessionManager::get_sessions_list( + [], + [], + null, + null, + api_get_current_access_url_id(), + [] +); + +foreach ($sessionsList as $session) { + $sessions[$session['id']] = $session['name']; +} + +$servicesList = $plugin->getAllServices(); + +foreach ($servicesList as $service) { + $services[$service['id']] = $service['name']; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); + +// Build the form +$form = new FormValidator('add_coupon'); +$form->addText('code', $plugin->get_lang('CouponCode'), true); +$form->addRadio('discount_type', $plugin->get_lang('CouponDiscountType'), $discountTypes); +$form->addElement( + 'number', + 'discount_amount', + [$plugin->get_lang('CouponDiscount'), null, $currencyIso], + ['step' => 1] +); +$form->addDateRangePicker('date', get_lang('Date'), true); +$form->addCheckBox('active', get_lang('Active')); +$form->addElement( + 'advmultiselect', + 'courses', + get_lang('Courses'), + $courses +); + +if ($includeSession) { + $form->addElement( + 'advmultiselect', + 'sessions', + get_lang('Sessions'), + $sessions + ); +} + +if ($includeServices) { + $form->addElement( + 'advmultiselect', + 'services', + get_lang('Services'), + $services + ); +} + +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->exportValues(); + + $coupon['code'] = $formValues['code']; + $coupon['discount_type'] = $formValues['discount_type']; + $coupon['discount_amount'] = $formValues['discount_amount']; + $coupon['valid_start'] = $formValues['date_start']; + $coupon['valid_end'] = $formValues['date_end']; + $coupon['active'] = $formValues['active']; + + if ($coupon['discount_type'] == BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE && $coupon['discount_amount'] > 100) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponDiscountExceed100'), 'error', false) + ); + } + + $coupon['courses'] = isset($formValues['courses']) ? $formValues['courses'] : []; + $coupon['sessions'] = isset($formValues['sessions']) ? $formValues['sessions'] : []; + $coupon['services'] = isset($formValues['services']) ? $formValues['services'] : []; + + $result = $plugin->addNewCoupon($coupon); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/coupons.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$formDefaults = [ + 'code' => '', + 'discount_type' => null, + 'discount_amount' => 0, + 'active' => 0, + 'courses' => [], + 'sessions' => [], + 'services' => [], +]; + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('CouponAdd'); +$interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'coupons.php', + 'name' => $plugin->get_lang('CouponList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/coupons.php b/plugin/buycourses/src/coupons.php new file mode 100644 index 000000000..60c574437 --- /dev/null +++ b/plugin/buycourses/src/coupons.php @@ -0,0 +1,109 @@ +getCouponInfo($_GET['coupon_id']); + + if (empty($coupon)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'deactivate': + //activate coupon + break; + case 'activate': + //deactivate coupon + break; + } + + header("Location: $urlToRedirect"); + exit; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); +$couponStatuses = $plugin->getCouponStatuses(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + } + + if ($selectedFilterType === false) { + $selectedFilterType = '0'; + } +} + +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('CouponStatus'), $couponStatuses); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, +]); + +$coupons = $plugin->getCouponsListByStatus($selectedStatus); +$currency = $plugin->getSelectedCurrency(); + +foreach ($coupons as &$coupon) { + if ($coupon['discount_type'] == BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $coupon['discount_value'] = $coupon['discount_amount']." %"; + } elseif ($coupon['discount_type'] == BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_AMOUNT) { + $coupon['discount_value'] = $plugin->getPriceWithCurrencyFromIsoCode($coupon['discount_amount'], $currency['iso_code']); + } + $coupon['discount_type'] = $discountTypes[$coupon['discount_type']]; +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('CouponList'); +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fas fa-plus'). + $plugin->get_lang('CouponAdd'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/coupon_add.php', + ['class' => 'btn btn-primary'] +); + +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$template->assign('form', $form->returnForm()); +$template->assign('selected_status', $selectedStatus); +$template->assign('coupon_list', $coupons); +$template->assign('coupon_status_active', BuyCoursesPlugin::COUPON_STATUS_ACTIVE); +$template->assign('coupon_status_disable', BuyCoursesPlugin::COUPON_STATUS_DISABLE); + +$content = $template->fetch('buycourses/view/coupons.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/course_catalog.php b/plugin/buycourses/src/course_catalog.php new file mode 100644 index 000000000..02c1d8fb2 --- /dev/null +++ b/plugin/buycourses/src/course_catalog.php @@ -0,0 +1,100 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$nameFilter = ''; +$minFilter = 0; +$maxFilter = 0; + +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $minFilter = isset($formValues['min']) ? $formValues['min'] : 0; + $maxFilter = isset($formValues['max']) ? $formValues['max'] : 0; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); +$form->addText('name', get_lang('CourseName'), false); +$form->addElement( + 'number', + 'min', + $plugin->get_lang('MinimumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$form->addElement( + 'number', + 'max', + $plugin->get_lang('MaximumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$courseList = $plugin->getCatalogCourseList($first, $pageSize, $nameFilter, $minFilter, $maxFilter); +$totalItems = $plugin->getCatalogCourseList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'list.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} else { + $interbreadcrumb[] = [ + 'url' => 'course_panel.php', + 'name' => get_lang('TabsDashboard'), + ]; +} + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('CourseListOnSale'); +$tpl = new Template($templateName); +$tpl->assign('search_filter_form', $form->returnForm()); +$tpl->assign('showing_courses', true); +$tpl->assign('courses', $courseList); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('pagination', $pagination); + +$countSessions = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); + +$tpl->assign('coursesExist', true); +$tpl->assign('sessionExist', $countSessions > 0); + +$content = $tpl->fetch('buycourses/view/catalog.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/course_panel.php b/plugin/buycourses/src/course_panel.php new file mode 100644 index 000000000..7a2d027f8 --- /dev/null +++ b/plugin/buycourses/src/course_panel.php @@ -0,0 +1,67 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$userInfo = api_get_user_info(); + +$productTypes = $plugin->getProductTypes(); +$saleStatuses = $plugin->getSaleStatuses(); +$paymentTypes = $plugin->getPaymentTypes(); + +$sales = $plugin->getSaleListByUserId($userInfo['id']); + +$saleList = []; + +foreach ($sales as $sale) { + if ($sale['product_type'] == 1) { + $saleList[] = [ + 'id' => $sale['id'], + 'reference' => $sale['reference'], + 'date' => api_format_date($sale['date'], DATE_TIME_FORMAT_LONG_24H), + 'currency' => $sale['iso_code'], + 'price' => $sale['price'], + 'product_name' => $sale['product_name'], + 'product_type' => $productTypes[$sale['product_type']], + 'payment_type' => $paymentTypes[$sale['payment_type']], + ]; + } +} + +$toolbar = Display::toolbarButton( + $plugin->get_lang('CourseListOnSale'), + 'course_catalog.php', + 'search-plus', + 'primary', + ['title' => $plugin->get_lang('CourseListOnSale')] +); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = get_lang('TabsDashboard'); +$tpl = new Template($templateName); +$tpl->assign('showing_courses', true); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('sale_list', $saleList); + +$content = $tpl->fetch('buycourses/view/course_panel.tpl'); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/error.php b/plugin/buycourses/src/error.php new file mode 100644 index 000000000..f0ef55241 --- /dev/null +++ b/plugin/buycourses/src/error.php @@ -0,0 +1,14 @@ +addDatePicker('date_start', get_lang('DateStart'), false); +$form->addDatePicker('date_end', get_lang('DateEnd'), false); +$form->addButton('export_sales', get_lang('ExportExcel'), 'check', 'primary'); +$salesStatus = []; + +if ($form->validate()) { + $reportValues = $form->getSubmitValues(); + + $dateStart = $reportValues['date_start']; + $dateEnd = $reportValues['date_end']; + + if ($dateStart == null || $dateEnd == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('SelectDateRange'), 'error', false) + ); + } elseif ($dateStart > $dateEnd) { + Display::addFlash( + Display::return_message(get_lang('EndDateCannotBeBeforeTheStartDate'), 'error', false) + ); + } else { + $salesStatus = $plugin->getSaleListReport($dateStart, $dateEnd); + } +} + +if (!empty($salesStatus)) { + $archiveFile = 'export_report_sales_'.api_get_local_time(); + Export::arrayToXls($salesStatus, $archiveFile); +} +$interbreadcrumb[] = [ + 'url' => '../index.php', 'name' => $plugin->get_lang('plugin_title'), +]; +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/src/sales_report.php', + 'name' => $plugin->get_lang('SalesReport'), +]; + +$templateName = $plugin->get_lang('ExportReport'); +$toolbar = Display::url( + Display::return_icon('back.png', get_lang('GoBack'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/sales_report.php' +); +$template = new Template($templateName); +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$content = $template->fetch('buycourses/view/export_report.tpl'); +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/export_subscription_report.php b/plugin/buycourses/src/export_subscription_report.php new file mode 100644 index 000000000..ba81299e8 --- /dev/null +++ b/plugin/buycourses/src/export_subscription_report.php @@ -0,0 +1,63 @@ +addDatePicker('date_start', get_lang('DateStart'), false); +$form->addDatePicker('date_end', get_lang('DateEnd'), false); +$form->addButton('export_sales', get_lang('ExportExcel'), 'check', 'primary'); +$salesStatus = []; + +if ($form->validate()) { + $reportValues = $form->getSubmitValues(); + + $dateStart = $reportValues['date_start']; + $dateEnd = $reportValues['date_end']; + + if ($dateStart == null || $dateEnd == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('SelectDateRange'), 'error', false) + ); + } elseif ($dateStart > $dateEnd) { + Display::addFlash( + Display::return_message(get_lang('EndDateCannotBeBeforeTheStartDate'), 'error', false) + ); + } else { + $salesStatus = $plugin->getSubscriptionSaleListReport($dateStart, $dateEnd); + } +} + +if (!empty($salesStatus)) { + $archiveFile = 'export_report_sales_'.api_get_local_time(); + Export::arrayToXls($salesStatus, $archiveFile); +} +$interbreadcrumb[] = [ + 'url' => '../index.php', 'name' => $plugin->get_lang('plugin_title'), +]; +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscription_sales_report.php', + 'name' => $plugin->get_lang('SubscriptionSalesReport'), +]; + +$templateName = $plugin->get_lang('ExportReport'); +$toolbar = Display::url( + Display::return_icon('back.png', get_lang('GoBack'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscription_sales_report.php' +); +$template = new Template($templateName); +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$content = $template->fetch('buycourses/view/export_report.tpl'); +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/expresscheckout.php b/plugin/buycourses/src/expresscheckout.php new file mode 100644 index 000000000..43fb80e2e --- /dev/null +++ b/plugin/buycourses/src/expresscheckout.php @@ -0,0 +1,47 @@ +get('unregistered_users_enable'); + +$userIsAdmin = api_is_platform_admin(); + +if (($allow === 'true' && api_is_anonymous()) || !api_is_anonymous()) { + $webPluginPath = api_get_path(WEB_PLUGIN_PATH).'buycourses/'; + + $countCourses = $plugin->getCatalogCourseList( + 0, + BuyCoursesPlugin::PAGINATION_PAGE_SIZE, + null, + 0, + 0, + 'count' + ); + + if ($countCourses > 0 && !$userIsAdmin) { + api_location($webPluginPath.'src/course_catalog.php'); + } + + $countSessions = $plugin->getCatalogSessionList( + 0, + BuyCoursesPlugin::PAGINATION_PAGE_SIZE, + null, + 0, + 0, + 'count' + ); + + if ($countSessions > 0 && !$userIsAdmin) { + api_location($webPluginPath.'src/session_catalog.php'); + } + + $htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + + $tpl = new Template(); + $content = $tpl->fetch('buycourses/view/index.tpl'); + $tpl->assign('content', $content); + $tpl->display_one_col_template(false); +} diff --git a/plugin/buycourses/src/index.php b/plugin/buycourses/src/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/buycourses/src/index.php @@ -0,0 +1 @@ +get('invoicing_enable') === 'true'; +if (!$invoicingEnable) { + api_not_allowed(true, $plugin->get_lang('NoInvoiceEnable')); +} + +$saleId = isset($_GET['invoice']) ? (int) $_GET['invoice'] : 0; +$isService = isset($_GET['is_service']) ? (int) $_GET['is_service'] : 0; + +$globalParameters = $plugin->getGlobalParameters(); +$infoSale = $plugin->getDataSaleInvoice($saleId, $isService); +$buyer = api_get_user_info($infoSale['user_id']); +$extraUserInfoData = UserManager::get_extra_user_data($infoSale['user_id']); +$infoInvoice = $plugin->getDataInvoice($saleId, $isService); + +$taxAppliesTo = $globalParameters['tax_applies_to']; +$taxEnable = $plugin->get('tax_enable') === 'true' && + ($taxAppliesTo == BuyCoursesPlugin::TAX_APPLIES_TO_ALL || + ($taxAppliesTo == BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_COURSE && !$isService) || + ($taxAppliesTo == BuyCoursesPlugin::TAX_APPLIES_TO_ONLY_SESSION && $isService)); + +$htmlText = ''; +$htmlText .= ''; +$htmlText .= ''; +$htmlText .= ''; + +$organization = ChamiloApi::getPlatformLogo('', [], true); +// Use custom logo image. +$pdfLogo = api_get_setting('pdf_logo_header'); +if ($pdfLogo === 'true') { + $visualTheme = api_get_visual_theme(); + $img = api_get_path(SYS_CSS_PATH).'themes/'.$visualTheme.'/images/pdf_logo_header.png'; + if (file_exists($img)) { + $organization = ""; + } +} +$htmlText .= $organization; + +// Seller and customer info +$htmlText .= ''; +$htmlText .= ''; +$htmlText .= ''; +$htmlText .= ''; +$htmlText .= ''; +$htmlText .= '
'; +$htmlText .= ''.$globalParameters['seller_name'].'
'; +$htmlText .= $globalParameters['seller_id'].'
'; +$htmlText .= $globalParameters['seller_address'].'
'; +$htmlText .= $globalParameters['seller_email'].'
'; +$htmlText .= '
'; +$htmlText .= ''.$buyer['complete_name'].'
'; +$htmlText .= ($extraUserInfoData['buycourses_company'] ? $extraUserInfoData['buycourses_company'].'
' : ''); +$htmlText .= ($extraUserInfoData['buycourses_vat'] ? $extraUserInfoData['buycourses_vat'].'
' : ''); +$htmlText .= ($extraUserInfoData['buycourses_address'] ? $extraUserInfoData['buycourses_address'].'
' : ''); +$htmlText .= ($buyer['phone'] ? $buyer['phone'].'
' : ''); +$htmlText .= ($buyer['email'] ? $buyer['email'].'
' : ''); +$htmlText .= '
'; + +$htmlText .= '

'; +$htmlText .= '

'; +$htmlText .= $plugin->get_lang('InvoiceDate').': ' + .api_convert_and_format_date($infoInvoice['date_invoice'], DATE_TIME_FORMAT_LONG_24H).'
'; +$htmlText .= $plugin->get_lang('InvoiceNumber').': ' + .$infoInvoice['serie'].$infoInvoice['year'].'/'.$infoInvoice['num_invoice'].'
'; +$htmlText .= '



'; + +$header = [ + $plugin->get_lang('OrderReference'), + $plugin->get_lang('ProductType'), + $plugin->get_lang('Price'), +]; + +if ($taxEnable) { + $header[] = $globalParameters['tax_name']; + $header[] = $plugin->get_lang('Total'); +} + +$data = []; +$row = [ + $infoSale['reference'], + $infoSale['product_name'], +]; + +//var_dump($infoSale);exit; +$isoCode = $plugin->getCurrency($infoSale['currency_id'])['iso_code']; + +if ($taxEnable) { + $row[] = $plugin->getPriceWithCurrencyFromIsoCode($infoSale['price_without_tax'], $isoCode); + $row[] = $plugin->getPriceWithCurrencyFromIsoCode($infoSale['tax_amount'], $isoCode).' ('.(int) $infoSale['tax_perc'].'%)'; +} + +$totalPrice = $plugin->getPriceWithCurrencyFromIsoCode( + $infoSale['price'], + $plugin->getCurrency($infoSale['currency_id'])['iso_code'] +); + +$row[] = $totalPrice; +$data[] = $row; + +$totalPrice = $plugin->getPriceWithCurrencyFromIsoCode( + $infoSale['price'], + $plugin->getCurrency($infoSale['currency_id'])['iso_code'] +); + +if ($taxEnable) { + $row = [ + '', + '', + '', + $plugin->get_lang('TotalPayout'), + $totalPrice, + ]; +} else { + $row = [ + '', + $plugin->get_lang('TotalPayout'), + $totalPrice, + ]; +} +$data[] = $row; +$attr = []; +$attr['class'] = 'table table-hover table-striped data_table'; +$attr['width'] = '100%'; +$htmlText .= Display::table($header, $data, $attr); +$htmlText .= ''; + +$fileName = $infoInvoice['serie'].$infoInvoice['year'].'-'.$infoInvoice['num_invoice']; +$fileName = api_replace_dangerous_char($fileName); +$params = [ + 'filename' => $fileName, + 'pdf_title' => $plugin->get_lang('Invoice'), + 'pdf_description' => '', + 'format' => 'A4', + 'orientation' => 'P', +]; +$pdf = new PDF($params['format'], $params['orientation'], $params); +@$pdf->content_to_pdf($htmlText, '', $fileName, null, 'D', false, null, false, false, false); +exit; diff --git a/plugin/buycourses/src/list.php b/plugin/buycourses/src/list.php new file mode 100644 index 000000000..eb6386669 --- /dev/null +++ b/plugin/buycourses/src/list.php @@ -0,0 +1,87 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; +$taxEnable = $plugin->get('tax_enable') === 'true'; + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$type = isset($_GET['type']) ? (int) $_GET['type'] : BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +$qb = $plugin->getCourseList($first, $pageSize); +$query = $qb->getQuery(); +$courses = new Paginator($query, $fetchJoinCollection = true); +foreach ($courses as $course) { + $item = $plugin->getItemByProduct($course->getId(), BuyCoursesPlugin::PRODUCT_TYPE_COURSE); + $course->buyCourseData = []; + if ($item !== false) { + $course->buyCourseData = $item; + } +} + +$totalItems = count($courses); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => $type] +); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$templateName = $plugin->get_lang('AvailableCourses'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$tpl = new Template($templateName); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('courses', $courses); +$tpl->assign('course_pagination', $pagination); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('tax_enable', $taxEnable); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('buycourses/view/list.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/list_coupon.php b/plugin/buycourses/src/list_coupon.php new file mode 100644 index 000000000..8f9aacd9e --- /dev/null +++ b/plugin/buycourses/src/list_coupon.php @@ -0,0 +1,96 @@ +getCouponInfo($_GET['coupon_id']); + + if (empty($coupon)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'deactivate': + //activate coupon + break; + case 'activate': + //deactivate coupon + break; + } + + header("Location: $urlToRedirect"); + exit; +} + +$discountTypes = $plugin->getCouponDiscountTypes(); +$couponStatuses = $plugin->getCouponStatuses(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::COUPON_STATUS_ACTIVE; + } + + if ($selectedFilterType === false) { + $selectedFilterType = '0'; + } +} + +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('CouponStatus'), $couponStatuses); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, +]); + +$coupons = $plugin->getCouponsListByStatus($selectedStatus); + +foreach ($coupons as &$coupon) { + if ($coupon['discount_type'] == BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_PERCENTAGE) { + $coupon['discount_value'] = $coupon['discount_amount']." %"; + } elseif ($coupon['discount_type'] == BuyCoursesPlugin::COUPON_DISCOUNT_TYPE_AMOUNT) { + $coupon['discount_value'] = $plugin->getPriceWithCurrencyFromIsoCode($coupon['discount_amount'], $coupon['iso_code']); + } + $coupon['discount_type'] = $discountTypes[$coupon['discount_type']]; +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('CouponList'); +$template = new Template($templateName); + +$template->assign('form', $form->returnForm()); +$template->assign('selected_status', $selectedStatus); +$template->assign('coupon_list', $coupons); +$template->assign('coupon_status_active', BuyCoursesPlugin::COUPON_STATUS_ACTIVE); +$template->assign('coupon_status_disable', BuyCoursesPlugin::COUPON_STATUS_DISABLE); + +$content = $template->fetch('buycourses/view/list_coupon.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/list_service.php b/plugin/buycourses/src/list_service.php new file mode 100644 index 000000000..051f3d61e --- /dev/null +++ b/plugin/buycourses/src/list_service.php @@ -0,0 +1,71 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; +if (!$includeServices) { + api_not_allowed(true); +} + +$taxEnable = $plugin->get('tax_enable') === 'true'; + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +$services = $plugin->getServices($first, $pageSize); +$totalItems = $plugin->getServices(0, 1000000000, 'count'); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$templateName = $plugin->get_lang('Services'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$tpl = new Template($templateName); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('tax_enable', $taxEnable); +$tpl->assign('services', $services); +$tpl->assign('service_pagination', $pagination); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('buycourses/view/list.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/list_session.php b/plugin/buycourses/src/list_session.php new file mode 100644 index 000000000..cf380ab0c --- /dev/null +++ b/plugin/buycourses/src/list_session.php @@ -0,0 +1,89 @@ +get('include_sessions') === 'true'; + +if (!$includeSession) { + api_not_allowed(true); +} +$includeServices = $plugin->get('include_services') === 'true'; +$taxEnable = $plugin->get('tax_enable') === 'true'; + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('Sessions'); + +$tpl = new Template($templateName); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('tax_enable', $taxEnable); + +$query = CoursesAndSessionsCatalog::browseSessions(null, ['start' => $first, 'length' => $pageSize], true); +$sessions = new Paginator($query, $fetchJoinCollection = true); +foreach ($sessions as $session) { + $item = $plugin->getItemByProduct($session->getId(), BuyCoursesPlugin::PRODUCT_TYPE_SESSION); + $session->buyCourseData = []; + if ($item !== false) { + $session->buyCourseData = $item; + } +} + +$totalItems = count($sessions); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION] +); + +$tpl->assign('sessions', $sessions); +$tpl->assign('session_pagination', $pagination); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('buycourses/view/list.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/panel.ajax.php b/plugin/buycourses/src/panel.ajax.php new file mode 100644 index 000000000..a968c7ddb --- /dev/null +++ b/plugin/buycourses/src/panel.ajax.php @@ -0,0 +1,221 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); + +$action = isset($_GET['a']) ? $_GET['a'] : null; + +switch ($action) { + case 'saleInfo': + //$saleId is only used in getSale() and is always filtered there + $saleId = isset($_POST['id']) ? $_POST['id'] : ''; + $sale = $plugin->getSale($saleId); + $productType = ($sale['product_type'] == 1) ? get_lang('Course') : get_lang('Session'); + $paymentType = ($sale['payment_type'] == 1) ? 'Paypal' : $plugin->get_lang('BankTransfer'); + $productInfo = ($sale['product_type'] == 1) + ? api_get_course_info_by_id($sale['product_id']) + : api_get_session_info($sale['product_id']); + $currency = $plugin->getSelectedCurrency(); + if ($sale['product_type'] == 1) { + $productImage = $productInfo['course_image_large']; + } else { + $productImage = ($productInfo['image']) + ? $productInfo['image'] + : Template::get_icon_path('session_default.png'); + } + + $userInfo = api_get_user_info($sale['user_id']); + + $html = '

'.$sale['product_name'].'

'; + $html .= '
'; + $html .= '
'; + $html .= '
    '; + $html .= '
  • '.$plugin->get_lang('OrderPrice').': '.$sale['price'].'
  • '; + $html .= '
  • '.$plugin->get_lang('CurrencyType').': '.$currency['iso_code'].'
  • '; + $html .= '
  • '.$plugin->get_lang('ProductType').': '.$productType.'
  • '; + $html .= '
  • '.$plugin->get_lang('OrderDate').': ' + .api_format_date($sale['date'], DATE_TIME_FORMAT_LONG_24H).'
  • '; + $html .= '
  • '.$plugin->get_lang('Buyer').': '.$userInfo['complete_name'].'
  • '; + $html .= '
  • '.$plugin->get_lang('PaymentMethods').': '.$paymentType.'
  • '; + $html .= '
'; + $html .= '
'; + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= '
'; + echo $html; + break; + case 'stats': + $stats = []; + $stats['completed_count'] = 0; + $stats['completed_total_amount'] = 0; + $stats['pending_count'] = 0; + $stats['pending_total_amount'] = 0; + $stats['canceled_count'] = 0; + $stats['canceled_total_amount'] = 0; + + $completedPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED); + $pendingPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_PENDING); + $canceledPayouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_CANCELED); + $currency = $plugin->getSelectedCurrency(); + + foreach ($completedPayouts as $completed) { + $stats['completed_count'] = count($completedPayouts); + $stats['completed_total_amount'] += $completed['commission']; + $stats['completed_total_amount'] = number_format($stats['completed_total_amount'], 2); + } + + foreach ($pendingPayouts as $pending) { + $stats['pending_count'] = count($pendingPayouts); + $stats['pending_total_amount'] += $pending['commission']; + $stats['pending_total_amount'] = number_format($stats['pending_total_amount'], 2); + } + + foreach ($canceledPayouts as $canceled) { + $stats['canceled_count'] = count($canceledPayouts); + $stats['canceled_total_amount'] += $canceled['commission']; + $stats['canceled_total_amount'] = number_format($stats['canceled_total_amount'], 2); + } + + $html = ' +
+

+

    +
  • + '.get_plugin_lang("PayoutsTotalCompleted", "BuyCoursesPlugin").' + '.$stats['completed_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' + '.$stats['completed_total_amount'].' '.$currency['iso_code'].' +
  • +
  • '.get_plugin_lang("PayoutsTotalPending", "BuyCoursesPlugin").' + '.$stats['pending_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' + '.$stats['pending_total_amount'].' '.$currency['iso_code'].' +
  • +
  • '.get_plugin_lang("PayoutsTotalCanceled", "BuyCoursesPlugin").' + '.$stats['canceled_count'].' - '.get_plugin_lang("TotalAmount", "BuyCoursesPlugin").' + '.$stats['canceled_total_amount'].' '.$currency['iso_code'].' +
  • +
+

+
+ '; + echo $html; + break; + case 'processPayout': + if (api_is_anonymous()) { + break; + } + + $html = ''; + $allPays = []; + $totalAccounts = 0; + $totalPayout = 0; + $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; + + if (!$payouts) { + echo Display::return_message(get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), 'error', false); + + break; + } + + foreach ($payouts as $index => $id) { + $allPays[] = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_PENDING, $id); + } + + foreach ($allPays as $payout) { + $totalPayout += number_format($payout['commission'], 2); + $totalAccounts++; + } + + $currentCurrency = $plugin->getSelectedCurrency(); + + $isoCode = $currentCurrency['iso_code']; + + $html .= '

'.get_plugin_lang("VerifyTotalAmountToProceedPayout", "BuyCoursesPlugin").'

'; + $html .= ' +

+

    +
  • '.get_plugin_lang("TotalAcounts", "BuyCoursesPlugin").' '.$totalAccounts.'
  • +
  • '.get_plugin_lang("TotalPayout", "BuyCoursesPlugin").' '.$isoCode.' '.$totalPayout.'
  • +
+

+

'.get_plugin_lang("CautionThisProcessCantBeCanceled", "BuyCoursesPlugin").'

+

+
+ '; + + echo $html; + break; + + case 'proceedPayout': + if (api_is_anonymous()) { + break; + } + + $paypalParams = $plugin->getPaypalParams(); + $pruebas = $paypalParams['sandbox'] == 1; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + require_once "paypalfunctions.php"; + $allPayouts = []; + $totalAccounts = 0; + $totalPayout = 0; + + $payouts = isset($_POST['payouts']) ? $_POST['payouts'] : ''; + + if (!$payouts) { + echo Display::return_message(get_plugin_lang("SelectOptionToProceed", "BuyCoursesPlugin"), 'error', false); + + break; + } + + foreach ($payouts as $index => $id) { + $allPayouts[] = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_PENDING, $id); + } + + $currentCurrency = $plugin->getSelectedCurrency(); + + $isoCode = $currentCurrency['iso_code']; + + $result = MassPayment($allPayouts, $isoCode); + + if ($result['ACK'] === 'Success') { + foreach ($allPayouts as $payout) { + $plugin->setStatusPayouts($payout['id'], BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED); + } + echo Display::return_message(get_plugin_lang("PayoutSuccess", "BuyCoursesPlugin"), 'success', false); + } else { + echo Display::return_message( + ''.$result['L_SEVERITYCODE0'].' '.$result['L_ERRORCODE0'].' - ' + .$result['L_SHORTMESSAGE0'].'
  • '.$result['L_LONGMESSAGE0'].'
', + 'error', + false + ); + } + break; + + case 'cancelPayout': + if (api_is_anonymous()) { + break; + } + + $payoutId = isset($_POST['id']) ? $_POST['id'] : ''; + $plugin->setStatusPayouts($payoutId, BuyCoursesPlugin::PAYOUT_STATUS_CANCELED); + echo ''; + break; +} +exit; diff --git a/plugin/buycourses/src/paymentsetup.php b/plugin/buycourses/src/paymentsetup.php new file mode 100644 index 000000000..6983c4e10 --- /dev/null +++ b/plugin/buycourses/src/paymentsetup.php @@ -0,0 +1,538 @@ +get('paypal_enable'); +$transferEnable = $plugin->get('transfer_enable'); +$tpvRedsysEnable = $plugin->get('tpv_redsys_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); +$culqiEnable = $plugin->get('culqi_enable'); +$stripeEnable = $plugin->get('stripe_enable') === 'true'; +$cecabankEnable = $plugin->get('cecabank_enable') === 'true'; + +if (isset($_GET['action'], $_GET['id'])) { + if ($_GET['action'] == 'delete_taccount') { + $plugin->deleteTransferAccount($_GET['id']); + + Display::addFlash( + Display::return_message(get_lang('ItemRemoved'), 'success') + ); + + header('Location: '.api_get_self()); + exit; + } +} + +$globalSettingForm = new FormValidator('currency'); + +if ($globalSettingForm->validate()) { + $globalSettingFormValues = $globalSettingForm->getSubmitValues(); + + $plugin->saveCurrency($globalSettingFormValues['currency']); + unset($globalSettingFormValues['currency']); + $plugin->saveGlobalParameters($globalSettingFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$currencies = $plugin->getCurrencies(); + +$currencySelect = $globalSettingForm->addSelect( + 'currency', + [ + $plugin->get_lang('CurrencyType'), + $plugin->get_lang('InfoCurrency'), + ], + [get_lang('Select')] +); + +foreach ($currencies as $currency) { + $currencyText = implode( + ' => ', + [ + $currency['country_name'], + $currency['iso_code'], + ] + ); + $currencyValue = $currency['id']; + $currencySelect->addOption($currencyText, $currencyValue); + + if ($currency['status']) { + $currencySelect->setSelected($currencyValue); + } +} + +$globalSettingForm->addTextarea( + 'terms_and_conditions', + [ + get_lang('TermsAndConditions'), + $plugin->get_lang('WriteHereTheTermsAndConditionsOfYourECommerce'), + ] +); + +$globalSettingForm->addElement( + 'text', + 'sale_email', + $plugin->get_lang('SaleEmail') +); + +$taxEnable = $plugin->get('tax_enable') === 'true'; +$invoicingEnable = $plugin->get('invoicing_enable') === 'true'; + +if ($taxEnable) { + $globalSettingForm->addHtml('
'); + + $globalSettingForm->addElement( + 'number', + 'global_tax_perc', + [$plugin->get_lang('GlobalTaxPerc'), $plugin->get_lang('GlobalTaxPercDescription'), '%'], + ['step' => 1] + ); + + $taxAppliesTo = $plugin->getTaxAppliesTo(); + + $taxTypeSelect = $globalSettingForm->addSelect( + 'tax_applies_to', + $plugin->get_lang('TaxAppliesTo'), + [get_lang('Select')] + ); + + foreach ($taxAppliesTo as $key => $value) { + $optionText = $value; + $optionyValue = $key; + + $taxTypeSelect->addOption($optionText, $optionyValue); + } + + $globalSettingForm->addElement( + 'text', + 'tax_name', + $plugin->get_lang('TaxNameCustom'), + ['placeholder' => $plugin->get_lang('TaxNameExamples')] + ); +} + +if ($invoicingEnable) { + $globalSettingForm->addHtml('
'); + + $globalSettingForm->addElement( + 'text', + 'seller_name', + $plugin->get_lang('SellerName') + ); + + $globalSettingForm->addElement( + 'text', + 'seller_id', + $plugin->get_lang('SellerId') + ); + + $globalSettingForm->addElement( + 'text', + 'seller_address', + $plugin->get_lang('SellerAddress') + ); + + $globalSettingForm->addElement( + 'text', + 'seller_email', + $plugin->get_lang('SellerEmail') + ); + + $globalSettingForm->addElement( + 'number', + 'next_number_invoice', + [$plugin->get_lang('NextNumberInvoice'), $plugin->get_lang('NextNumberInvoiceDescription')], + ['step' => 1] + ); + + $globalSettingForm->addElement( + 'text', + 'invoice_series', + [$plugin->get_lang('InvoiceSeries'), $plugin->get_lang('InvoiceSeriesDescription')] + ); +} + +$globalSettingForm->addButtonSave(get_lang('Save')); +$globalSettingForm->setDefaults($plugin->getGlobalParameters()); + +$termsAndConditionsForm = new FormValidator('termsconditions'); + +$paypalForm = new FormValidator('paypal'); + +if ($paypalForm->validate()) { + $paypalFormValues = $paypalForm->getSubmitValues(); + + $plugin->savePaypalParams($paypalFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$paypalForm->addText( + 'username', + $plugin->get_lang('ApiUsername'), + false, + ['cols-size' => [3, 8, 1]] +); +$paypalForm->addText( + 'password', + $plugin->get_lang('ApiPassword'), + false, + ['cols-size' => [3, 8, 1]] +); +$paypalForm->addText( + 'signature', + $plugin->get_lang('ApiSignature'), + false, + ['cols-size' => [3, 8, 1]] +); +$paypalForm->addCheckBox('sandbox', null, $plugin->get_lang('Sandbox')); +$paypalForm->addButtonSave(get_lang('Save')); +$paypalForm->setDefaults($plugin->getPaypalParams()); + +// TPV Redsys +$htmlTpvRedsys = Display::return_message($plugin->get_lang('NotFindRedsysFile'), 'warning', false); +if (file_exists(api_get_path(SYS_PLUGIN_PATH).'buycourses/resources/apiRedsys.php')) { + $tpvRedsysForm = new FormValidator('tpv_redsys'); + $tpvRedsysForm->addHtml( + Display::return_message($plugin->get_lang('InfoTpvRedsysApiCredentials'), 'info', false) + ); + + if ($tpvRedsysForm->validate()) { + $tpvRedsysFormValues = $tpvRedsysForm->getSubmitValues(); + + $plugin->saveTpvRedsysParams($tpvRedsysFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; + } + + $tpvRedsysForm->addText( + 'merchantcode', + [$plugin->get_lang('DS_MERCHANT_MERCHANTCODE'), 'DS_MERCHANT_MERCHANTCODE'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'terminal', + [$plugin->get_lang('DS_MERCHANT_TERMINAL'), 'DS_MERCHANT_TERMINAL'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'currency', + [$plugin->get_lang('DS_MERCHANT_CURRENCY'), 'DS_MERCHANT_CURRENCY'], + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'kc', + $plugin->get_lang('kc'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'url_redsys', + $plugin->get_lang('url_redsys'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addText( + 'url_redsys_sandbox', + $plugin->get_lang('url_redsys_sandbox'), + false, + ['cols-size' => [3, 8, 1]] + ); + $tpvRedsysForm->addCheckBox('sandbox', null, $plugin->get_lang('Sandbox')); + $tpvRedsysForm->addButtonSave(get_lang('Save')); + $tpvRedsysForm->setDefaults($plugin->getTpvRedsysParams()); + + $htmlTpvRedsys = $tpvRedsysForm->returnForm(); +} + +// Platform Commissions + +$commissionForm = new FormValidator('commissions'); + +if ($commissionForm->validate()) { + $commissionFormValues = $commissionForm->getSubmitValues(); + + $plugin->updateCommission($commissionFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$commissionForm->addElement( + 'number', + 'commission', + [$plugin->get_lang('Commission'), null, '%'], + ['step' => 1, 'cols-size' => [3, 7, 1], 'min' => 0, 'max' => 100] +); + +$commissionForm->addButtonSave(get_lang('Save')); +$commissionForm->setDefaults($plugin->getPlatformCommission()); + +$transferForm = new FormValidator('transfer_account'); + +if ($transferForm->validate()) { + $transferFormValues = $transferForm->getSubmitValues(); + + $plugin->saveTransferAccount($transferFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$transferForm->addText( + 'tname', + get_lang('Name'), + false, + ['cols-size' => [3, 8, 1]] +); +$transferForm->addText( + 'taccount', + $plugin->get_lang('BankAccount'), + false, + ['cols-size' => [3, 8, 1]] +); +$transferForm->addText( + 'tswift', + [$plugin->get_lang('SWIFT'), $plugin->get_lang('SWIFT_help')], + false, + ['cols-size' => [3, 8, 1]] +); +$transferForm->addButtonCreate(get_lang('Add')); + +$transferAccounts = $plugin->getTransferAccounts(); + +$transferInfoForm = new FormValidator('transfer_info'); + +if ($transferInfoForm->validate()) { + $transferInfoFormValues = $transferInfoForm->getSubmitValues(); + + $plugin->saveTransferInfoEmail($transferInfoFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} +$transferInfoForm->addHtmlEditor( + 'tinfo_email_extra', + $plugin->get_lang('InfoEmailExtra'), + false, + false, + ['ToolbarSet' => 'Minimal'] +); +$transferInfoForm->addButtonCreate(get_lang('Save')); +$transferInfoForm->setDefaults($plugin->getTransferInfoExtra()); + +// Culqi main configuration + +$culqiForm = new FormValidator('culqi_config'); + +if ($culqiForm->validate()) { + $culqiFormValues = $culqiForm->getSubmitValues(); + + $plugin->saveCulqiParameters($culqiFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$culqiForm->addText( + 'commerce_code', + $plugin->get_lang('CommerceCode'), + false, + ['cols-size' => [3, 8, 1]] +); +$culqiForm->addText( + 'api_key', + $plugin->get_lang('ApiPassword'), + false, + ['cols-size' => [3, 8, 1]] +); +$culqiForm->addCheckBox('integration', null, $plugin->get_lang('Sandbox')); +$culqiForm->addButtonSave(get_lang('Save')); +$culqiForm->setDefaults($plugin->getCulqiParams()); + +// Stripe main configuration + +$stripeForm = new FormValidator('stripe_config'); + +if ($stripeForm->validate()) { + $stripeFormValues = $stripeForm->getSubmitValues(); + + $plugin->saveStripeParameters($stripeFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$stripeForm->addText( + 'account_id', + $plugin->get_lang('StripeAccountId'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addText( + 'secret_key', + $plugin->get_lang('StripeSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addText( + 'endpoint_secret', + $plugin->get_lang('StripeEndpointSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$stripeForm->addButtonSave(get_lang('Save')); +$stripeForm->setDefaults($plugin->getStripeParams()); + +// Cecabank main configuration + +$cecabankForm = new FormValidator('cecabank_config'); + +if ($cecabankForm->validate()) { + $cecabankFormValues = $cecabankForm->getSubmitValues(); + + $plugin->saveCecabankParameters($cecabankFormValues); + + Display::addFlash( + Display::return_message(get_lang('Saved'), 'success') + ); + + header('Location:'.api_get_self()); + exit; +} + +$cecabankForm->addText( + 'crypto_key', + $plugin->get_lang('CecaSecret'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'url', + $plugin->get_lang('CecaUrl'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'merchant_id', + $plugin->get_lang('CecaMerchanId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'acquirer_bin', + $plugin->get_lang('CecaAcquirerId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'terminal_id', + $plugin->get_lang('CecaTerminalId'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'cypher', + $plugin->get_lang('CecaCypher'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'exponent', + $plugin->get_lang('CecaExponent'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addText( + 'supported_payment', + $plugin->get_lang('CecaSupportedPayment'), + false, + ['cols-size' => [3, 8, 1]] +); +$cecabankForm->addButtonSave(get_lang('Save')); +$cecabankForm->setDefaults($plugin->getCecabankParams()); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('PaymentsConfiguration'); +$tpl = new Template($templateName); +$tpl->assign('header', $templateName); +$tpl->assign('global_config_form', $globalSettingForm->returnForm()); +$tpl->assign('paypal_form', $paypalForm->returnForm()); +$tpl->assign('commission_form', $commissionForm->returnForm()); +$tpl->assign('transfer_form', $transferForm->returnForm()); +$tpl->assign('transfer_info_form', $transferInfoForm->returnForm()); +$tpl->assign('culqi_form', $culqiForm->returnForm()); +$tpl->assign('transfer_accounts', $transferAccounts); +$tpl->assign('paypal_enable', $paypalEnable); +$tpl->assign('commissions_enable', $commissionsEnable); +$tpl->assign('transfer_enable', $transferEnable); +$tpl->assign('culqi_enable', $culqiEnable); +$tpl->assign('tpv_redsys_enable', $tpvRedsysEnable); +$tpl->assign('tpv_redsys_form', $htmlTpvRedsys); +$tpl->assign('stripe_enable', $stripeEnable); +$tpl->assign('stripe_form', $stripeForm->returnForm()); +$tpl->assign('cecabank_enable', $cecabankEnable); +$tpl->assign('cecabank_form', $cecabankForm->returnForm()); + +$content = $tpl->fetch('buycourses/view/paymentsetup.tpl'); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/payout_panel.php b/plugin/buycourses/src/payout_panel.php new file mode 100644 index 000000000..f972fed32 --- /dev/null +++ b/plugin/buycourses/src/payout_panel.php @@ -0,0 +1,65 @@ +get('include_sessions') === 'true'; + +$userInfo = api_get_user_info(); + +$payouts = $plugin->getPayouts(BuyCoursesPlugin::PAYOUT_STATUS_COMPLETED, 0, $userInfo['id']); + +$payoutList = []; + +foreach ($payouts as $payout) { + $payoutList[] = [ + 'id' => $payout['id'], + 'sale_id' => $payout['sale_id'], + 'reference' => $payout['sale_reference'], + 'date' => api_format_date($payout['date'], DATE_TIME_FORMAT_LONG_24H), + 'payout_date' => ($payout['payout_date'] === '0000-00-00 00:00:00') + ? '-' + : api_format_date($payout['payout_date'], DATE_TIME_FORMAT_LONG_24H), + 'currency' => $payout['iso_code'], + 'price' => $payout['item_price'], + 'commission' => $payout['commission'], + 'paypal_account' => $payout['paypal_account'], + 'status' => $payout['status'], + ]; +} + +$toolbar = Display::toolbarButton( + $plugin->get_lang('CourseListOnSale'), + 'course_catalog.php', + 'search-plus', + 'primary', + ['title' => $plugin->get_lang('CourseListOnSale')] +); + +$templateName = get_lang('TabsDashboard'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$tpl = new Template($templateName); +$tpl->assign('showing_courses', true); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('payout_list', $payoutList); + +$content = $tpl->fetch('buycourses/view/payout_panel.tpl'); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/payout_report.php b/plugin/buycourses/src/payout_report.php new file mode 100644 index 000000000..36fa0d7ef --- /dev/null +++ b/plugin/buycourses/src/payout_report.php @@ -0,0 +1,94 @@ +'; + +api_protect_admin_script(true); + +$plugin = BuyCoursesPlugin::create(); + +$commissionsEnable = $plugin->get('commissions_enable'); +$payoutStatuses = $plugin->getPayoutStatuses(); +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_COMPLETED; + +if ($commissionsEnable !== "true") { + api_not_allowed(true); +} + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::PAYOUT_STATUS_PENDING; + } +} + +$form->addSelect('status', $plugin->get_lang('PayoutStatus'), $payoutStatuses); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'status' => $selectedStatus, +]); + +switch ($selectedStatus) { + case '2': + $payouts = $plugin->getPayouts($selectedStatus); + + break; + case '1': + $payouts = $plugin->getPayouts($selectedStatus); + + break; + case '0': + default: + $payouts = $plugin->getPayouts(); + + break; +} + +$payoutList = []; + +foreach ($payouts as $payout) { + $payoutList[] = [ + 'id' => $payout['id'], + 'sale_id' => $payout['sale_id'], + 'reference' => $payout['sale_reference'], + 'date' => api_format_date($payout['date'], DATE_TIME_FORMAT_LONG_24H), + 'payout_date' => ($payout['payout_date'] === '0000-00-00 00:00:00') + ? '-' + : api_format_date($payout['payout_date'], DATE_TIME_FORMAT_LONG_24H), + 'currency' => $payout['iso_code'], + 'price' => $payout['item_price'], + 'commission' => $payout['commission'], + 'beneficiary' => api_get_person_name($payout['firstname'], $payout['lastname']), + 'paypal_account' => $payout['paypal_account'], + 'status' => $payout['status'], + ]; +} + +$templateName = $plugin->get_lang('PayoutReport'); + +$template = new Template($templateName); + +$template->assign('form', $form->returnForm()); +$template->assign('payout_list', $payoutList); +$template->assign('selected_status', $selectedStatus); + +$content = $template->fetch('buycourses/view/payout_report.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/paypal_payout.php b/plugin/buycourses/src/paypal_payout.php new file mode 100644 index 000000000..17a3dd2e1 --- /dev/null +++ b/plugin/buycourses/src/paypal_payout.php @@ -0,0 +1,55 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); + +if ($paypalEnable !== "true" && $commissionsEnable !== "true") { + api_not_allowed(true); +} + +$payouts = $plugin->getPayouts(); + +$payoutList = []; + +foreach ($payouts as $payout) { + $payoutList[] = [ + 'id' => $payout['id'], + 'reference' => $payout['sale_reference'], + 'date' => api_format_date($payout['date'], DATE_TIME_FORMAT_LONG_24H), + 'currency' => $payout['iso_code'], + 'price' => $payout['item_price'], + 'commission' => $payout['commission'], + 'paypal_account' => $payout['paypal_account'], + ]; +} + +$templateName = $plugin->get_lang('PaypalPayoutCommissions'); + +$template = new Template($templateName); + +$template->assign('payout_list', $payoutList); + +$content = $template->fetch('buycourses/view/paypal_payout.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/paypalfunctions.php b/plugin/buycourses/src/paypalfunctions.php new file mode 100644 index 000000000..8bb0e4fe7 --- /dev/null +++ b/plugin/buycourses/src/paypalfunctions.php @@ -0,0 +1,432 @@ + with your API Username + * Replace with your API Password + * Replace with your Signature. + */ +$API_UserName = $paypalUsername; +$API_Password = $paypalPassword; +$API_Signature = $paypalSignature; + +// BN Code is only applicable for partners +$sBNCode = "PP-ECWizard"; + +/** + * Define the PayPal Redirect URLs. + * This is the URL that the buyer is first sent to do authorize payment with their paypal account + * change the URL depending if you are testing on the sandbox or the live PayPal site. + * + * For the sandbox, the URL is https://www.sandbox.paypal.com/webscr&cmd=_express-checkout&token= + * For the live site, the URL is https://www.paypal.com/webscr&cmd=_express-checkout&token= + */ +if ($SandboxFlag == true) { + $API_Endpoint = "https://api-3t.sandbox.paypal.com/nvp"; + $PAYPAL_URL = "https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&token="; +} else { + $API_Endpoint = "https://api-3t.paypal.com/nvp"; + $PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token="; +} + +$USE_PROXY = false; +$version = "93"; + +if (session_id() == "") { + session_start(); +} + +/** + * An express checkout transaction starts with a token, that + * identifies to PayPal your transaction + * In this example, when the script sees a token, the script + * knows that the buyer has already authorized payment through + * paypal. If no token was found, the action is to send the buyer + * to PayPal to first authorize payment. + */ + +/** + * Purpose: Prepares the parameters for the SetExpressCheckout API Call. + * Inputs: + * paymentAmount: Total value of the shopping cart + * currencyCodeType: Currency code value the PayPal API + * paymentType: paymentType has to be one of the following values: Sale or Order or Authorization + * returnURL: the page where buyers return to after they are done with the payment review on PayPal + * cancelURL: the page where buyers return to when they cancel the payment review on PayPal. + */ +function CallShortcutExpressCheckout($paymentAmount, $currencyCodeType, $paymentType, $returnURL, $cancelURL, $extra) +{ + // Construct the parameter string that describes the SetExpressCheckout API call in the shortcut implementation + $nvpstr = "&PAYMENTREQUEST_0_AMT=".$paymentAmount; + $nvpstr .= "&PAYMENTREQUEST_0_ITEMAMT=".$paymentAmount; + $nvpstr .= "&PAYMENTREQUEST_0_PAYMENTACTION=".$paymentType; + $nvpstr .= "&RETURNURL=".$returnURL; + $nvpstr .= "&CANCELURL=".$cancelURL; + $nvpstr .= "&PAYMENTREQUEST_0_CURRENCYCODE=".$currencyCodeType; + $nvpstr .= $extra; + + $_SESSION["currencyCodeType"] = $currencyCodeType; + $_SESSION["PaymentType"] = $paymentType; + + /** + * Make the API call to PayPal + * If the API call succeded, then redirect the buyer to PayPal to begin to authorize payment. + * If an error occured, show the resulting errors. + */ + $resArray = hash_call("SetExpressCheckout", $nvpstr); + $ack = strtoupper($resArray["ACK"]); + if ($ack == "SUCCESS" || $ack == "SUCCESSWITHWARNING") { + $token = urldecode($resArray["TOKEN"]); + $_SESSION['TOKEN'] = $token; + } + + return $resArray; +} + +/** + * Purpose: Prepares the parameters for the SetExpressCheckout API Call. + * Inputs: + * paymentAmount: Total value of the shopping cart + * currencyCodeType: Currency code value the PayPal API + * paymentType: paymentType has to be one of the following values: Sale or Order or Authorization + * returnURL: the page where buyers return to after they are done with the payment review on PayPal + * cancelURL: the page where buyers return to when they cancel the payment review on PayPal + * shipToName: the Ship to name entered on the merchant's site + * shipToStreet: the Ship to Street entered on the merchant's site + * shipToCity: the Ship to City entered on the merchant's site + * shipToState: the Ship to State entered on the merchant's site + * shipToCountryCode: the Code for Ship to Country entered on the merchant's site + * shipToZip: the Ship to ZipCode entered on the merchant's site + * shipToStreet2: the Ship to Street2 entered on the merchant's site + * phoneNum: the phoneNum entered on the merchant's site. + */ +function CallMarkExpressCheckout( + $paymentAmount, + $currencyCodeType, + $paymentType, + $returnURL, + $cancelURL, + $shipToName, + $shipToStreet, + $shipToCity, + $shipToState, + $shipToCountryCode, + $shipToZip, + $shipToStreet2, + $phoneNum +) { + // Construct the parameter string that describes the SetExpressCheckout API call in the shortcut implementation + $nvpstr = "&PAYMENTREQUEST_0_AMT=".$paymentAmount; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_PAYMENTACTION=".$paymentType; + $nvpstr = $nvpstr."&RETURNURL=".$returnURL; + $nvpstr = $nvpstr."&CANCELURL=".$cancelURL; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_CURRENCYCODE=".$currencyCodeType; + $nvpstr = $nvpstr."&ADDROVERRIDE=1"; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTONAME=".$shipToName; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOSTREET=".$shipToStreet; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOSTREET2=".$shipToStreet2; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOCITY=".$shipToCity; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOSTATE=".$shipToState; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=".$shipToCountryCode; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOZIP=".$shipToZip; + $nvpstr = $nvpstr."&PAYMENTREQUEST_0_SHIPTOPHONENUM=".$phoneNum; + + $_SESSION["currencyCodeType"] = $currencyCodeType; + $_SESSION["PaymentType"] = $paymentType; + + /** + * Make the API call to PayPal + * If the API call succeded, then redirect the buyer to PayPal to begin to authorize payment. + * If an error occured, show the resulting errors. + */ + $resArray = hash_call("SetExpressCheckout", $nvpstr); + $ack = strtoupper($resArray["ACK"]); + if ($ack == "SUCCESS" || $ack == "SUCCESSWITHWARNING") { + $token = urldecode($resArray["TOKEN"]); + $_SESSION['TOKEN'] = $token; + } + + return $resArray; +} + +/** + * Purpose: Prepares the parameters for the GetExpressCheckoutDetails API Call. + * + * Inputs: + * None + * Returns: + * The NVP Collection object of the GetExpressCheckoutDetails Call Response. + */ +function GetShippingDetails($token) +{ + /** + * At this point, the buyer has completed authorizing the payment + * at PayPal. The function will call PayPal to obtain the details + * of the authorization, including any shipping information of the + * buyer. Remember, the authorization is not a completed transaction + * at this state - the buyer still needs an additional step to finalize + * the transaction. + * + * Build a second API request to PayPal, using the token as the + * ID to get the details on the payment authorization + */ + $nvpstr = "&TOKEN=".$token; + + /** + * Make the API call and store the results in an array. + * If the call was a success, show the authorization details, and provide + * an action to complete the payment. + * If failed, show the error. + */ + $resArray = hash_call("GetExpressCheckoutDetails", $nvpstr); + $ack = strtoupper($resArray["ACK"]); + if ($ack == "SUCCESS" || $ack == "SUCCESSWITHWARNING") { + $_SESSION['payer_id'] = $resArray['PAYERID']; + } + + return $resArray; +} + +/** + * Purpose: Prepares the parameters for the GetExpressCheckoutDetails API Call. + * Inputs: + * sBNCode: The BN code used by PayPal to track the transactions from a given shopping cart. + * Returns: + * The NVP Collection object of the GetExpressCheckoutDetails Call Response. + */ +function ConfirmPayment($FinalPaymentAmt) +{ + /** + * Gather the information to make the final call to + * finalize the PayPal payment. The variable nvpstr + * holds the name value pairs. + */ + + //Format the other parameters that were stored in the session from the previous calls + + $token = urlencode($_SESSION['TOKEN']); + $paymentType = urlencode($_SESSION['PaymentType']); + $currencyCodeType = urlencode($_SESSION['currencyCodeType']); + $payerID = urlencode($_SESSION['payer_id']); + $serverName = urlencode($_SERVER['SERVER_NAME']); + + $nvpstr = + '&TOKEN='.$token.'&PAYERID='.$payerID.'&PAYMENTREQUEST_0_PAYMENTACTION='.$paymentType.'&PAYMENTREQUEST_0_AMT=' + .$FinalPaymentAmt; + $nvpstr .= '&PAYMENTREQUEST_0_CURRENCYCODE='.$currencyCodeType.'&IPADDRESS='.$serverName; + $nvpstr = '&'.http_build_query([ + 'TOKEN' => $token, + 'PAYERID' => $payerID, + 'PAYMENTACTION' => $paymentType, + 'PAYMENTREQUEST_0_AMT' => $FinalPaymentAmt, + 'PAYMENTREQUEST_0_CURRENCYCODE' => $currencyCodeType, + 'IPADDRESS' => $serverName, + 'paymentactionspecified' => 'true', + ]); + + /** + * Make the call to PayPal to finalize payment + * If an error occured, show the resulting errors. + */ + $resArray = hash_call("DoExpressCheckoutPayment", $nvpstr); + + /** + * Display the API response back to the browser. + * If the response from PayPal was a success, display the response parameters + * If the response was an error, display the errors received using APIError.php. + */ + $ack = strtoupper($resArray["ACK"]); + + return $resArray; +} + +/** + * Purpose: This function makes a DoDirectPayment API call + * Inputs: + * paymentType: paymentType has to be one of the following values: Sale or Order or Authorization + * paymentAmount: total value of the shopping cart + * currencyCode: currency code value the PayPal API + * firstName: first name as it appears on credit card + * lastName: last name as it appears on credit card + * street: buyer's street address line as it appears on credit card + * city: buyer's city + * state: buyer's state + * countryCode: buyer's country code + * zip: buyer's zip + * creditCardType: buyer's credit card type (i.e. Visa, MasterCard ... ) + * creditCardNumber: buyers credit card number without any spaces, dashes or any other characters + * expDate: credit card expiration date + * cvv2: Card Verification Value + * Returns: + * The NVP Collection object of the DoDirectPayment Call Response. + */ +function DirectPayment( + $paymentType, + $paymentAmount, + $creditCardType, + $creditCardNumber, + $expDate, + $cvv2, + $firstName, + $lastName, + $street, + $city, + $state, + $zip, + $countryCode, + $currencyCode +) { + //Construct the parameter string that describes DoDirectPayment + $nvpstr = "&AMT=".$paymentAmount; + $nvpstr = $nvpstr."&CURRENCYCODE=".$currencyCode; + $nvpstr = $nvpstr."&PAYMENTACTION=".$paymentType; + $nvpstr = $nvpstr."&CREDITCARDTYPE=".$creditCardType; + $nvpstr = $nvpstr."&ACCT=".$creditCardNumber; + $nvpstr = $nvpstr."&EXPDATE=".$expDate; + $nvpstr = $nvpstr."&CVV2=".$cvv2; + $nvpstr = $nvpstr."&FIRSTNAME=".$firstName; + $nvpstr = $nvpstr."&LASTNAME=".$lastName; + $nvpstr = $nvpstr."&STREET=".$street; + $nvpstr = $nvpstr."&CITY=".$city; + $nvpstr = $nvpstr."&STATE=".$state; + $nvpstr = $nvpstr."&COUNTRYCODE=".$countryCode; + $nvpstr = $nvpstr."&IPADDRESS=".$_SERVER['REMOTE_ADDR']; + + $resArray = hash_call("DoDirectPayment", $nvpstr); + + return $resArray; +} + +/** + * Purpose: This function makes a MassPay API call + * Inputs: + * Beneficiarie: Array that contains the Beneficiearie paypal account and the payout amount + * Currency Code: The currency Iso code + * Returns: + * The NVP Collection object of the MassPay Call Response. + */ +function MassPayment(array $beneficiaries, $currencyCode) +{ + $nvpstr = "&RECEIVERTYPE=EmailAddress"; + $nvpstr .= "&CURRENCYCODE=".$currencyCode; + + $index = 0; + + foreach ($beneficiaries as $beneficiary) { + $nvpstr .= "&L_EMAIL".$index."=".$beneficiary['paypal_account']; + $nvpstr .= "&L_AMT".$index."=".$beneficiary['commission']; + $index++; + } + + $resArray = hash_call("MassPay", $nvpstr); + + return $resArray; +} + +/** + * hash_call: Function to perform the API call to PayPal using API signature. + * + * @methodName is name of API method. + * @nvpStr is nvp string. + * returns an associtive array containing the response from the server. + */ +function hash_call($methodName, $nvpStr) +{ + //declaring of global variables + global $API_Endpoint, $version, $API_UserName, $API_Password, $API_Signature; + global $USE_PROXY, $PROXY_HOST, $PROXY_PORT; + global $sBNCode; + + //setting the curl parameters. + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $API_Endpoint); + curl_setopt($ch, CURLOPT_VERBOSE, 1); + //turning off the server and peer verification(TrustManager Concept). + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1); + + //if USE_PROXY constant set to TRUE in Constants.php, then only proxy will be enabled. + //Set proxy name to PROXY_HOST and port number to PROXY_PORT in constants.php + if ($USE_PROXY) { + curl_setopt($ch, CURLOPT_PROXY, $PROXY_HOST.":".$PROXY_PORT); + } + + //NVPRequest for submitting to server + $nvpreq = "METHOD=".urlencode($methodName)."&VERSION=".urlencode($version). + "&PWD=".urlencode($API_Password)."&USER=".urlencode($API_UserName). + "&SIGNATURE=".urlencode($API_Signature).$nvpStr."&BUTTONSOURCE=".urlencode($sBNCode); + + //setting the nvpreq as POST FIELD to curl + curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq); + //getting response from server + $response = curl_exec($ch); + //converting NVPResponse to an Associative Array + $nvpResArray = deformatNVP($response); + $nvpReqArray = deformatNVP($nvpreq); + + $_SESSION['nvpReqArray'] = $nvpReqArray; + + if (curl_errno($ch)) { + // moving to display page to display curl errors + $_SESSION['curl_error_no'] = curl_errno($ch); + $_SESSION['curl_error_msg'] = curl_error($ch); + //Execute the Error handling module to display errors. + } else { + //closing the curl + curl_close($ch); + } + + return $nvpResArray; +} + +/** + * Purpose: Redirects to PayPal.com site. + * Inputs: NVP string. + */ +function RedirectToPayPal($token) +{ + global $PAYPAL_URL; + // Redirect to paypal.com here + $payPalURL = $PAYPAL_URL.$token; + header("Location: ".$payPalURL); + exit; +} + +/** + * This function will take NVPString and convert it to an Associative Array and it will decode the response. + * It is usefull to search for a particular key and displaying arrays. + * + * @nvpstr is NVPString. + * @nvpArray is Associative Array. + */ +function deformatNVP($nvpstr) +{ + $intial = 0; + $nvpArray = []; + + while (strlen($nvpstr)) { + //postion of Key + $keypos = strpos($nvpstr, '='); + //position of value + $valuepos = strpos($nvpstr, '&') ? strpos($nvpstr, '&') : strlen($nvpstr); + /*getting the Key and Value values and storing in a Associative Array*/ + $keyval = substr($nvpstr, $intial, $keypos); + $valval = substr($nvpstr, $keypos + 1, $valuepos - $keypos - 1); + //decoding the respose + $nvpArray[urldecode($keyval)] = urldecode($valval); + $nvpstr = substr($nvpstr, $valuepos + 1, strlen($nvpstr)); + } + + return $nvpArray; +} diff --git a/plugin/buycourses/src/process.php b/plugin/buycourses/src/process.php new file mode 100644 index 000000000..b2ccc9369 --- /dev/null +++ b/plugin/buycourses/src/process.php @@ -0,0 +1,188 @@ +'; +$plugin = BuyCoursesPlugin::create(); +$includeSession = $plugin->get('include_sessions') === 'true'; +$paypalEnabled = $plugin->get('paypal_enable') === 'true'; +$transferEnabled = $plugin->get('transfer_enable') === 'true'; +$culqiEnabled = $plugin->get('culqi_enable') === 'true'; +$tpvRedsysEnable = $plugin->get('tpv_redsys_enable') === 'true'; +$stripeEnable = $plugin->get('stripe_enable') === 'true'; +$tpvCecabankEnable = $plugin->get('cecabank_enable') === 'true'; + +if (!$paypalEnabled && !$transferEnabled && !$culqiEnabled && !$tpvRedsysEnable && !$stripeEnable && !$tpvCecabankEnable) { + api_not_allowed(true); +} + +if (!isset($_REQUEST['t'], $_REQUEST['i'])) { + api_not_allowed(true); +} + +$currency = $plugin->getSelectedCurrency(); +$buyingCourse = intval($_REQUEST['t']) === BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$buyingSession = intval($_REQUEST['t']) === BuyCoursesPlugin::PRODUCT_TYPE_SESSION; +$queryString = 'i='.intval($_REQUEST['i']).'&t='.intval($_REQUEST['t']); + +$coupon = null; + +if (isset($_REQUEST['c'])) { + $couponId = (int) $_REQUEST['c']; + if ($buyingCourse) { + $coupon = $plugin->getCoupon($couponId, BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCoupon($couponId, BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } +} + +if (empty($currentUserId)) { + Session::write('buy_course_redirect', api_get_self().'?'.$queryString); + header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php'); + exit; +} + +if ($buyingCourse) { + $courseInfo = $plugin->getCourseInfo($_REQUEST['i'], $coupon); + $item = $plugin->getItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +} elseif ($buyingSession) { + $sessionInfo = $plugin->getSessionInfo($_REQUEST['i'], $coupon); + $item = $plugin->getItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +} + +$form = new FormValidator('confirm_sale'); +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (!$formValues['payment_type']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + $saleId = $plugin->registerSale($item['id'], $formValues['payment_type'], $formValues['c']); + + if ($saleId !== false) { + $_SESSION['bc_sale_id'] = $saleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponSale($saleId, $formValues['c']); + if ($couponSaleId !== false) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process_confirm.php'); + } + + exit; +} + +$paymentTypesOptions = $plugin->getPaymentTypes(true); + +$count = count($paymentTypesOptions); +if ($count === 0) { + $form->addHtml($plugin->get_lang('NoPaymentOptionAvailable')); + $form->addHtml('
'); + $form->addHtml('
'); +} elseif ($count === 1) { + // get the only array item + foreach ($paymentTypesOptions as $type => $value) { + $form->addHtml(sprintf($plugin->get_lang('XIsOnlyPaymentMethodAvailable'), $value)); + $form->addHtml('
'); + $form->addHtml('
'); + $form->addHidden('payment_type', $type); + } +} else { + $form->addHtml( + Display::return_message( + $plugin->get_lang('PleaseSelectThePaymentMethodBeforeConfirmYourOrder'), + 'info' + ) + ); + $form->addRadio('payment_type', null, $paymentTypesOptions); +} + +$form->addHidden('t', intval($_REQUEST['t'])); +$form->addHidden('i', intval($_REQUEST['i'])); +if ($coupon != null) { + $form->addHidden('c', intval($coupon['id'])); +} +$form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success', 'btn-lg pull-right'); + +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + if ($coupon == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&c='.$coupon['id']); + + exit; +} +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', intval($_GET['t'])); +$formCoupon->addHidden('i', intval($_GET['i'])); +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); + +// View +$templateName = $plugin->get_lang('PaymentMethods'); +$interbreadcrumb[] = ['url' => 'course_catalog.php', 'name' => $plugin->get_lang('CourseListOnSale')]; + +$tpl = new Template($templateName); +$tpl->assign('item_type', (int) $_GET['t']); +$tpl->assign('buying_course', $buyingCourse); +$tpl->assign('buying_session', $buyingSession); +$tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); +$tpl->assign('form', $form->returnForm()); + +if ($buyingCourse) { + $tpl->assign('course', $courseInfo); +} elseif ($buyingSession) { + $tpl->assign('session', $sessionInfo); +} + +$content = $tpl->fetch('buycourses/view/process.tpl'); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/process_confirm.php b/plugin/buycourses/src/process_confirm.php new file mode 100644 index 000000000..ecb24794e --- /dev/null +++ b/plugin/buycourses/src/process_confirm.php @@ -0,0 +1,597 @@ +getSale($saleId); + +$coupon = []; +if (!empty($couponId)) { + $coupon = $plugin->getCoupon($couponId, $sale['product_type'], $sale['product_id']); +} + +$userInfo = api_get_user_info($sale['user_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$currency = $plugin->getCurrency($sale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +switch ($sale['payment_type']) { + case BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL: + $paypalParams = $plugin->getPaypalParams(); + + $pruebas = $paypalParams['sandbox'] == 1; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + + require_once "paypalfunctions.php"; + + $i = 0; + $extra = "&L_PAYMENTREQUEST_0_NAME0={$sale['product_name']}"; + $extra .= "&L_PAYMENTREQUEST_0_AMT0={$sale['price']}"; + $extra .= "&L_PAYMENTREQUEST_0_QTY0=1"; + + $expressCheckout = CallShortcutExpressCheckout( + $sale['price'], + $currency['iso_code'], + 'paypal', + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/success.php', + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/error.php', + $extra + ); + + if ($expressCheckout["ACK"] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + header('Location: ../index.php'); + exit; + } + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + RedirectToPayPal($expressCheckout["TOKEN"]); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + break; + } + + $transferAccounts = $plugin->getTransferAccounts(); + $infoEmailExtra = $plugin->getTransferInfoExtra()['tinfo_email_extra']; + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $messageTemplate = new Template(); + $messageTemplate->assign('user', $userInfo); + $messageTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + $messageTemplate->assign('transfer_accounts', $transferAccounts); + $messageTemplate->assign('info_email_extra', $infoEmailExtra); + + MessageManager::send_message_simple( + $userInfo['user_id'], + $plugin->get_lang('bc_subject'), + $messageTemplate->fetch('buycourses/view/message_transfer.tpl') + ); + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + Display::addFlash( + Display::return_message( + sprintf( + $plugin->get_lang('PurchaseStatusX'), + $plugin->get_lang('PendingReasonByTransfer') + ), + 'success', + false + ) + ); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', true); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: + // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded + // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported + $htmlHeadXtra[] = ''; + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + break; + } + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + } + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButton( + 'cancel', + $plugin->get_lang('CancelOrder'), + 'times', + 'danger', + 'default', + null, + ['id' => 'cancel'] + ); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', floatval($sale['price'])); + $template->assign('currency', $plugin->getSelectedCurrency()); + $template->assign('user', $userInfo); + $template->assign('sale', $sale); + $template->assign('form', $form->returnForm()); + $template->assign('is_culqi_payment', true); + $template->assign('culqi_params', $culqiParams = $plugin->getCulqiParams()); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS: + $tpvRedsysParams = $plugin->getTpvRedsysParams(); + + require_once '../resources/apiRedsys.php'; + $tpv = new RedsysAPI(); + + $merchantcode = $tpvRedsysParams['merchantcode']; + $terminal = $tpvRedsysParams['terminal']; + $currency = $tpvRedsysParams['currency']; + $transactionType = "0"; + $urlMerchant = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_response.php'; + $urlSuccess = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_success.php'; + $urlFailed = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_error.php'; + $order = str_pad(strval($saleId), 4, "0", STR_PAD_LEFT); + $amount = $sale['price'] * 100; + $description = $plugin->get_lang('OrderReference').": ".$sale['reference']; + $tpv->setParameter("DS_MERCHANT_AMOUNT", $amount); + $tpv->setParameter("DS_MERCHANT_ORDER", $order); + $tpv->setParameter("DS_MERCHANT_MERCHANTCODE", $merchantcode); + $tpv->setParameter("DS_MERCHANT_CURRENCY", $currency); + $tpv->setParameter("DS_MERCHANT_TRANSACTIONTYPE", $transactionType); + $tpv->setParameter("DS_MERCHANT_TERMINAL", $terminal); + $tpv->setParameter("DS_MERCHANT_MERCHANTURL", $urlMerchant); + $tpv->setParameter("DS_MERCHANT_URLOK", $urlSuccess); + $tpv->setParameter("DS_MERCHANT_URLKO", $urlFailed); + $tpv->setParameter("DS_MERCHANT_PRODUCTDESCRIPTION", $description); + + $version = "HMAC_SHA256_V1"; + $kc = $tpvRedsysParams['kc']; + + $urlTpv = $tpvRedsysParams['url_redsys']; + $sandboxFlag = $tpvRedsysParams['sandbox'] == 1; + if ($sandboxFlag === true) { + $urlTpv = $tpvRedsysParams['url_redsys_sandbox']; + } + + $params = $tpv->createMerchantParameters(); + $signature = $tpv->createMerchantSignature($kc); + + echo '
'; + echo ''; + echo ''; + echo ''; + echo '
'; + + echo ''; + + break; + case BuyCoursesPlugin::PAYMENT_TYPE_STRIPE: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id'], $coupon); + break; + } + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $stripeParams = $plugin->getStripeParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + \Stripe\Stripe::setApiKey($stripeParams['secret_key']); + \Stripe\Stripe::setAppInfo("ChamiloBuyCoursesPlugin"); + + $session = \Stripe\Checkout\Session::create([ + 'payment_method_types' => ['card'], + 'line_items' => [[ + 'price_data' => [ + 'unit_amount_decimal' => $sale['price'] * 100, + 'currency' => $currency['iso_code'], + 'product_data' => [ + 'name' => $sale['product_name'], + ], + ], + 'quantity' => 1, + ]], + 'customer_email' => $_SESSION['_user']['email'], + 'mode' => 'payment', + 'success_url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/src/stripe_success.php', + 'cancel_url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/src/stripe_cancel.php', + ]); + + if (!empty($session)) { + $plugin->updateSaleReference($saleId, $session->id); + + unset($_SESSION['bc_coupon_id']); + + header('HTTP/1.1 301 Moved Permanently'); + header('Location: '.$session->url); + } else { + Display::addFlash( + Display::return_message( + $plugin->get_lang('ErrorOccurred'), + 'error', + false + ) + ); + header('Location: ../index.php'); + } + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', false); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + break; + } + + $cecabankParams = $plugin->getcecabankParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $urlTpv = $cecabankParams['url']; + $currency = $plugin->getCurrency($sale['currency_id']); + $signature = $plugin->getCecabankSignature($sale['reference'], $sale['price']); + + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
'; + + echo ''; + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', false); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; +} diff --git a/plugin/buycourses/src/process_subscription_confirm.php b/plugin/buycourses/src/process_subscription_confirm.php new file mode 100644 index 000000000..2c334b8f8 --- /dev/null +++ b/plugin/buycourses/src/process_subscription_confirm.php @@ -0,0 +1,375 @@ +getSubscriptionSale($saleId); + +if (!empty($couponId)) { + $coupon = $plugin->getCoupon($couponId, $sale['product_type'], $sale['product_id']); +} + +$userInfo = api_get_user_info($sale['user_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$currency = $plugin->getCurrency($sale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +switch ($sale['payment_type']) { + case BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL: + $paypalParams = $plugin->getPaypalParams(); + + $pruebas = $paypalParams['sandbox'] == 1; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + + require_once "paypalfunctions.php"; + + $i = 0; + $extra = "&L_PAYMENTREQUEST_0_NAME0={$sale['product_name']}"; + $extra .= "&L_PAYMENTREQUEST_0_AMT0={$sale['price']}"; + $extra .= "&L_PAYMENTREQUEST_0_QTY0=1"; + + $expressCheckout = CallShortcutExpressCheckout( + $sale['price'], + $currency['iso_code'], + 'paypal', + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/success.php', + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/error.php', + $extra + ); + + if ($expressCheckout["ACK"] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + header('Location: ../index.php'); + exit; + } + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + RedirectToPayPal($expressCheckout["TOKEN"]); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getSubscriptionCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSubscriptionSessionInfo($sale['product_id'], $coupon); + break; + } + + $transferAccounts = $plugin->getTransferAccounts(); + $infoEmailExtra = $plugin->getTransferInfoExtra()['tinfo_email_extra']; + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSubscriptionSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $messageTemplate = new Template(); + $messageTemplate->assign('user', $userInfo); + $messageTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + $messageTemplate->assign('transfer_accounts', $transferAccounts); + $messageTemplate->assign('info_email_extra', $infoEmailExtra); + + MessageManager::send_message_simple( + $userInfo['user_id'], + $plugin->get_lang('bc_subject'), + $messageTemplate->fetch('buycourses/view/message_transfer.tpl') + ); + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + Display::addFlash( + Display::return_message( + sprintf( + $plugin->get_lang('PurchaseStatusX'), + $plugin->get_lang('PendingReasonByTransfer') + ), + 'success', + false + ) + ); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', $sale['price']); + $template->assign('currency', $sale['currency_id']); + $template->assign('user', $userInfo); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + $template->assign('is_bank_transfer', true); + + $content = $template->fetch('buycourses/view/subscription_process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: + // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded + // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported + $htmlHeadXtra[] = ''; + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getSubscriptionCourseInfo($sale['product_id'], $coupon); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSubscriptionSessionInfo($sale['product_id'], $coupon); + break; + } + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelSubscriptionSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + } + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButton( + 'cancel', + $plugin->get_lang('CancelOrder'), + 'times', + 'danger', + 'default', + null, + ['id' => 'cancel'] + ); + + $template = new Template(); + + if ($buyingCourse) { + $template->assign('course', $course); + } elseif ($buyingSession) { + $template->assign('session', $session); + } + + $template->assign('buying_course', $buyingCourse); + $template->assign('buying_session', $buyingSession); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $sale['product_name']); + $template->assign('price', floatval($sale['price'])); + $template->assign('currency', $plugin->getSelectedCurrency()); + $template->assign('user', $userInfo); + $template->assign('sale', $sale); + $template->assign('form', $form->returnForm()); + $template->assign('is_culqi_payment', true); + $template->assign('culqi_params', $culqiParams = $plugin->getCulqiParams()); + + $content = $template->fetch('buycourses/view/subscription_process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_REDSYS: + $tpvRedsysParams = $plugin->getTpvRedsysParams(); + + require_once '../resources/apiRedsys.php'; + $tpv = new RedsysAPI(); + + $merchantcode = $tpvRedsysParams['merchantcode']; + $terminal = $tpvRedsysParams['terminal']; + $currency = $tpvRedsysParams['currency']; + $transactionType = "0"; + $urlMerchant = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_response.php'; + $urlSuccess = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_success.php'; + $urlFailed = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/tpv_error.php'; + $order = str_pad(strval($saleId), 4, "0", STR_PAD_LEFT); + $amount = $sale['price'] * 100; + $description = $plugin->get_lang('OrderReference').": ".$sale['reference']; + $tpv->setParameter("DS_MERCHANT_AMOUNT", $amount); + $tpv->setParameter("DS_MERCHANT_ORDER", $order); + $tpv->setParameter("DS_MERCHANT_MERCHANTCODE", $merchantcode); + $tpv->setParameter("DS_MERCHANT_CURRENCY", $currency); + $tpv->setParameter("DS_MERCHANT_TRANSACTIONTYPE", $transactionType); + $tpv->setParameter("DS_MERCHANT_TERMINAL", $terminal); + $tpv->setParameter("DS_MERCHANT_MERCHANTURL", $urlMerchant); + $tpv->setParameter("DS_MERCHANT_URLOK", $urlSuccess); + $tpv->setParameter("DS_MERCHANT_URLKO", $urlFailed); + $tpv->setParameter("DS_MERCHANT_PRODUCTDESCRIPTION", $description); + + $version = "HMAC_SHA256_V1"; + $kc = $tpvRedsysParams['kc']; + + $urlTpv = $tpvRedsysParams['url_redsys']; + $sandboxFlag = $tpvRedsysParams['sandbox'] == 1; + if ($sandboxFlag === true) { + $urlTpv = $tpvRedsysParams['url_redsys_sandbox']; + } + + $params = $tpv->createMerchantParameters(); + $signature = $tpv->createMerchantSignature($kc); + + echo '
'; + echo ''; + echo ''; + echo ''; + echo '
'; + + echo ''; + + break; +} diff --git a/plugin/buycourses/src/sales_report.php b/plugin/buycourses/src/sales_report.php new file mode 100644 index 000000000..e66fcfb60 --- /dev/null +++ b/plugin/buycourses/src/sales_report.php @@ -0,0 +1,216 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); +$includeServices = $plugin->get('include_services'); +$invoicingEnable = $plugin->get('invoicing_enable') === 'true'; + +if (isset($_GET['order'])) { + $sale = $plugin->getSale($_GET['order']); + if (empty($sale)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'confirm': + $plugin->completeSale($sale['id']); + $plugin->storePayouts($sale['id']); + Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_COMPLETED, + 'sale' => $sale['id'], + ]); + break; + case 'cancel': + $plugin->cancelSale($sale['id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning' + ) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_CANCELED, + 'sale' => $sale['id'], + ]); + break; + } + + header("Location: $urlToRedirect"); + exit; +} + +$productTypes = $plugin->getProductTypes(); +$saleStatuses = $plugin->getSaleStatuses(); +$paymentTypes = $plugin->getPaymentTypes(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_PENDING; +$selectedSale = isset($_GET['sale']) ? intval($_GET['sale']) : 0; +$dateStart = isset($_GET['date_start']) ? $_GET['date_start'] : date('Y-m-d H:i', mktime(0, 0, 0)); +$dateEnd = isset($_GET['date_end']) ? $_GET['date_end'] : date('Y-m-d H:i', mktime(23, 59, 59)); +$searchTerm = ''; +$email = ''; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedFilterType = $form->getSubmitValue('filter_type'); + $selectedStatus = $form->getSubmitValue('status'); + $searchTerm = $form->getSubmitValue('user'); + $dateStart = $form->getSubmitValue('date_start'); + $dateEnd = $form->getSubmitValue('date_end'); + $email = $form->getSubmitValue('email'); + + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::SALE_STATUS_PENDING; + } + + if ($selectedFilterType === false) { + $selectedFilterType = '0'; + } +} + +$form->addRadio( + 'filter_type', + get_lang('Filter'), + [ + $plugin->get_lang('ByStatus'), + $plugin->get_lang('ByUser'), + $plugin->get_lang('ByDate'), + $plugin->get_lang('ByEmail'), + ] +); +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('OrderStatus'), $saleStatuses); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('user', get_lang('UserName'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addDateRangePicker('date', get_lang('Date'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('email', get_lang('Email'), false); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, + 'date_start' => $dateStart, + 'date_end' => $dateEnd, + 'email' => $email, +]); + +switch ($selectedFilterType) { + case '0': + $sales = $plugin->getSaleListByStatus($selectedStatus); + break; + case '1': + $sales = $plugin->getSaleListByUser($searchTerm); + break; + case '2': + $sales = $plugin->getSaleListByDate($dateStart, $dateEnd); + break; + case '3': + $sales = $plugin->getSaleListByEmail($email); + break; +} + +foreach ($sales as &$sale) { + $sale['product_type'] = $productTypes[$sale['product_type']]; + $sale['payment_type'] = $paymentTypes[$sale['payment_type']]; + $sale['complete_user_name'] = api_get_person_name($sale['firstname'], $sale['lastname']); + $sale['num_invoice'] = $plugin->getNumInvoice($sale['id'], 0); + $sale['total_price'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['price'], $sale['iso_code']); + if (isset($sale['discount_amount']) && $sale['discount_amount'] != 0) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getSaleCouponCode($sale['id']); + } +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_jqgrid_js(); +$htmlHeadXtra[] = BuyCoursesPlugin::getSalesReportScript($sales, $invoicingEnable); + +$templateName = $plugin->get_lang('SalesReport'); +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/export_report.php', + ['class' => 'btn btn-primary'] +); + +if ($paypalEnable === 'true' && $commissionsEnable === 'true') { + $toolbar .= Display::toolbarButton( + $plugin->get_lang('PaypalPayoutCommissions'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/paypal_payout.php', + 'paypal', + 'primary', + ['title' => $plugin->get_lang('PaypalPayoutCommissions')] + ); + + $template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) + ); +} + +if ($commissionsEnable === 'true') { + $toolbar .= Display::toolbarButton( + $plugin->get_lang('PayoutReport'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/payout_report.php', + 'money', + 'info', + ['title' => $plugin->get_lang('PayoutReport')] + ); + + $template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) + ); +} +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$template->assign('selected_sale', $selectedSale); +$template->assign('selected_status', $selectedStatus); +$template->assign('services_are_included', $includeServices); +$template->assign('sale_list', $sales); +$template->assign('sale_status_canceled', BuyCoursesPlugin::SALE_STATUS_CANCELED); +$template->assign('sale_status_pending', BuyCoursesPlugin::SALE_STATUS_PENDING); +$template->assign('sale_status_completed', BuyCoursesPlugin::SALE_STATUS_COMPLETED); +$template->assign('invoicing_enable', $invoicingEnable); + +$content = $template->fetch('buycourses/view/sales_report.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/service_catalog.php b/plugin/buycourses/src/service_catalog.php new file mode 100644 index 000000000..46929b3fc --- /dev/null +++ b/plugin/buycourses/src/service_catalog.php @@ -0,0 +1,101 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$nameFilter = null; +$minFilter = 0; +$maxFilter = 0; +$appliesToFilter = ''; + +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $minFilter = isset($formValues['min']) ? $formValues['min'] : 0; + $maxFilter = isset($formValues['max']) ? $formValues['max'] : 0; + $appliesToFilter = isset($formValues['applies_to']) ? $formValues['applies_to'] : ''; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); +$form->addText('name', $plugin->get_lang('ServiceName'), false); +$form->addElement( + 'number', + 'min', + $plugin->get_lang('MinimumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$form->addElement( + 'number', + 'max', + $plugin->get_lang('MaximumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$appliesToOptions = [ + '' => get_lang('Any'), + 0 => get_lang('None'), + 1 => get_lang('User'), + 2 => get_lang('Course'), + 3 => get_lang('Session'), + 4 => get_lang('TemplateTitleCertificate'), +]; +$form->addSelect('applies_to', $plugin->get_lang('AppliesTo'), $appliesToOptions); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$serviceList = $plugin->getCatalogServiceList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, $appliesToFilter); +$totalItems = $plugin->getCatalogServiceList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, $appliesToFilter, 'count'); +$pagesCount = ceil($totalItems / $pageSize); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'list.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} else { + $interbreadcrumb[] = [ + 'url' => '../index.php', + 'name' => $plugin->get_lang('UserPanel'), + ]; +} + +$templateName = $plugin->get_lang('ListOfServicesOnSale'); +$tpl = new Template($templateName); +$tpl->assign('search_filter_form', $form->returnForm()); +$tpl->assign('showing_services', true); +$tpl->assign('services', $serviceList); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('pagination', $pagination); + +$content = $tpl->fetch('buycourses/view/catalog.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/service_error.php b/plugin/buycourses/src/service_error.php new file mode 100644 index 000000000..43ebe9003 --- /dev/null +++ b/plugin/buycourses/src/service_error.php @@ -0,0 +1,35 @@ +getServiceSale($serviceSaleId); + + $plugin->cancelServiceSale(intval($serviceSaleId)); + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCancelled'), + 'error', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorOccurred'), 'error', false) +); + +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + +exit; diff --git a/plugin/buycourses/src/service_information.php b/plugin/buycourses/src/service_information.php new file mode 100644 index 000000000..f8f2f6522 --- /dev/null +++ b/plugin/buycourses/src/service_information.php @@ -0,0 +1,42 @@ + + * + * @package chamilo.buycourses_plugin + */ +$cidReset = true; + +require_once '../../../main/inc/global.inc.php'; + +$serviceId = isset($_GET['service_id']) ? intval($_GET['service_id']) : false; +$htmlHeadXtra[] = ''; +$plugin = BuyCoursesPlugin::create(); + +$includeServices = $plugin->get('include_services') === 'true'; + +if (!$includeServices) { + api_not_allowed(true); +} + +$service = $plugin->getService($serviceId); + +if (!$service['id']) { + api_not_allowed(true); +} + +$template = new Template(false); +$template->assign('pageUrl', api_get_path(WEB_PATH)."service/{$serviceId}/information/"); +$template->assign('service', $service); +$template->assign('essence', Essence\Essence::instance()); + +$content = $template->fetch('buycourses/view/service_information.tpl'); + +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/service_panel.php b/plugin/buycourses/src/service_panel.php new file mode 100644 index 000000000..78caecc8f --- /dev/null +++ b/plugin/buycourses/src/service_panel.php @@ -0,0 +1,74 @@ +get('include_services') === 'true'; +$includeSessions = $plugin->get('include_sessions') === 'true'; +$servicesOnly = $plugin->get('show_services_only') === 'true'; + +$userInfo = api_get_user_info(); + +if (!$userInfo) { + api_not_allowed(true); +} +$serviceTypes = $plugin->getServiceTypes(); +$serviceSaleStatuses['status_cancelled'] = BuyCoursesPlugin::SERVICE_STATUS_CANCELLED; +$serviceSaleStatuses['status_pending'] = BuyCoursesPlugin::SERVICE_STATUS_PENDING; +$serviceSaleStatuses['status_completed'] = BuyCoursesPlugin::SERVICE_STATUS_COMPLETED; + +$serviceSales = $plugin->getServiceSales($userInfo['user_id']); +$saleList = []; + +foreach ($serviceSales as $sale) { + $saleList[] = [ + 'id' => $sale['id'], + 'name' => $sale['service']['name'], + 'service_type' => $serviceTypes[$sale['service']['applies_to']], + 'applies_to' => $sale['service']['applies_to'], + 'reference' => $sale['reference'], + 'date' => api_format_date(api_get_local_time($sale['buy_date']), DATE_TIME_FORMAT_LONG_24H), + 'date_end' => api_format_date(api_get_local_time($sale['date_end']), DATE_TIME_FORMAT_LONG_24H), + 'currency' => $sale['currency'], + 'price' => $sale['price'], + 'status' => $sale['status'], + ]; +} + +$toolbar = Display::toolbarButton( + $plugin->get_lang('CourseListOnSale'), + 'course_catalog.php', + 'search-plus', + 'primary', + ['title' => $plugin->get_lang('CourseListOnSale')] +); + +$webPluginPath = api_get_path(WEB_PLUGIN_PATH); +$htmlHeadXtra[] = api_get_css($webPluginPath.'buycourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_js_simple($webPluginPath.'buycourses/resources/js/modals.js'); + +$templateName = $plugin->get_lang('TabsDashboard'); +$tpl = new Template($templateName); +$tpl->assign('showing_courses', true); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('service_sale_statuses', $serviceSaleStatuses); +$tpl->assign('sale_list', $saleList); +if ($servicesOnly) { + $tpl->assign('show_services_only', true); +} + +$content = $tpl->fetch('buycourses/view/service_panel.tpl'); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/service_process.php b/plugin/buycourses/src/service_process.php new file mode 100644 index 000000000..b383fa5c3 --- /dev/null +++ b/plugin/buycourses/src/service_process.php @@ -0,0 +1,304 @@ +'; +$em = Database::getManager(); +$plugin = BuyCoursesPlugin::create(); +$includeServices = $plugin->get('include_services'); +$additionalQueryString = ''; +if ($includeServices !== 'true') { + api_not_allowed(true); +} + +$typeUser = $type === BuyCoursesPlugin::SERVICE_TYPE_USER; +$typeCourse = $type === BuyCoursesPlugin::SERVICE_TYPE_COURSE; +$typeSession = $type === BuyCoursesPlugin::SERVICE_TYPE_SESSION; +$typeFinalLp = $type === BuyCoursesPlugin::SERVICE_TYPE_LP_FINAL_ITEM; +$queryString = 'i='.$serviceId.'&t='.$type.$additionalQueryString; + +if (isset($_REQUEST['c'])) { + $couponCode = $_REQUEST['c']; + $coupon = $plugin->getCouponServiceByCode($couponCode, $_REQUEST['i']); +} + +$serviceInfo = $plugin->getService($serviceId, $coupon); +$userInfo = api_get_user_info($currentUserId); + +$form = new FormValidator('confirm_sale'); +$paymentTypesOptions = $plugin->getPaymentTypes(true); + +$form->addHtml( + Display::return_message( + $plugin->get_lang('PleaseSelectThePaymentMethodBeforeConfirmYourOrder'), + 'info' + ) +); +$form->addRadio('payment_type', null, $paymentTypesOptions); + +$infoRequired = false; +if ($typeUser || $typeCourse || $typeSession || $typeFinalLp) { + $infoRequired = true; + $form->addHtml( + Display::return_message( + $plugin->get_lang('PleaseSelectTheCorrectInfoToApplyTheService'), + 'info' + ) + ); +} + +$selectOptions = [ + 0 => get_lang('None'), +]; + +if ($typeUser) { + $users = UserManager::getRepository()->findAll(); + $selectOptions[$userInfo['user_id']] = api_get_person_name( + $userInfo['firstname'], + $userInfo['lastname'] + ).' ('.get_lang('Myself').')'; + + if (!empty($users)) { + /** @var User $user */ + foreach ($users as $user) { + if (intval($userInfo['user_id']) !== intval($user->getId())) { + $selectOptions[$user->getId()] = $user->getCompleteNameWithUsername(); + } + } + } + $form->addSelect('info_select', get_lang('User'), $selectOptions); +} elseif ($typeCourse) { + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); + $courses = $user->getCourses(); + $checker = false; + foreach ($courses as $course) { + $checker = true; + $selectOptions[$course->getCourse()->getId()] = $course->getCourse()->getTitle(); + } + if (!$checker) { + $form->addHtml( + Display::return_message( + $plugin->get_lang('YouNeedToBeRegisteredInAtLeastOneCourse'), + 'error' + ) + ); + } + $form->addSelect('info_select', get_lang('Course'), $selectOptions); +} elseif ($typeSession) { + $sessions = []; + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); + $userSubscriptions = $user->getSessionCourseSubscriptions(); + + /** @var SessionRelCourseRelUser $userSubscription */ + foreach ($userSubscriptions as $userSubscription) { + $sessions[$userSubscription->getSession()->getId()] = $userSubscription->getSession()->getName(); + } + + $sessionsAsGeneralCoach = $user->getSessionAsGeneralCoach(); + /** @var Session $sessionAsGeneralCoach */ + foreach ($sessionsAsGeneralCoach as $sessionAsGeneralCoach) { + $sessions[$sessionAsGeneralCoach->getId()] = $sessionAsGeneralCoach->getName(); + } + + if (!$sessions) { + $form->addHtml(Display::return_message($plugin->get_lang('YouNeedToBeRegisteredInAtLeastOneSession'), 'error')); + } else { + $selectOptions = $sessions; + $form->addSelect('info_select', get_lang('Session'), $selectOptions); + } +} elseif ($typeFinalLp) { + // We need here to check the current user courses first + /** @var User $user */ + $user = UserManager::getRepository()->find($currentUserId); + $courses = $user->getCourses(); + $courseLpList = []; + $sessionLpList = []; + $checker = false; + foreach ($courses as $course) { + // Now get all the courses lp's + $thisLpList = $em->getRepository('ChamiloCourseBundle:CLp')->findBy(['cId' => $course->getCourse()->getId()]); + foreach ($thisLpList as $lp) { + $courseLpList[$lp->getCId()] = $lp->getName().' ('.$course->getCourse()->getTitle().')'; + } + } + + // Here now checking the current user sessions + $sessions = $user->getSessionCourseSubscriptions(); + foreach ($sessions as $session) { + $thisLpList = $em + ->getRepository('ChamiloCourseBundle:CLp') + ->findBy(['sessionId' => $session->getSession()->getId()]); + + // Here check all the lpItems + foreach ($thisLpList as $lp) { + $thisLpItems = $em->getRepository('ChamiloCourseBundle:CLpItem')->findBy(['lpId' => $lp->getId()]); + + foreach ($thisLpItems as $item) { + //Now only we need the final item and return the current LP + if ($item->getItemType() == TOOL_LP_FINAL_ITEM) { + $checker = true; + $sessionLpList[$lp->getCId()] = $lp->getName().' ('.$session->getSession()->getName().')'; + } + } + } + + $thisLpList = $em->getRepository('ChamiloCourseBundle:CLp')->findBy(['cId' => $session->getCourse()->getId()]); + + // Here check all the lpItems + foreach ($thisLpList as $lp) { + $thisLpItems = $em->getRepository('ChamiloCourseBundle:CLpItem')->findBy(['lpId' => $lp->getId()]); + foreach ($thisLpItems as $item) { + //Now only we need the final item and return the current LP + if ($item->getItemType() == TOOL_LP_FINAL_ITEM) { + $checker = true; + $sessionLpList[$lp->getCId()] = $lp->getName().' ('.$session->getSession()->getName().')'; + } + } + } + } + + $selectOptions = $selectOptions + $courseLpList + $sessionLpList; + if (!$checker) { + $form->addHtml( + Display::return_message( + $plugin->get_lang('YourCoursesNeedAtLeastOneLearningPath'), + 'error' + ) + ); + } + $form->addSelect('info_select', get_lang('LearningPath'), $selectOptions); +} + +$form->addHidden('t', intval($_GET['t'])); +$form->addHidden('i', intval($_GET['i'])); +$form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (!isset($formValues['payment_type'])) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + $infoSelected = []; + if ($infoRequired) { + if (isset($formValues['info_select'])) { + $infoSelected = $formValues['info_select']; + } else { + Display::addFlash( + Display::return_message($plugin->get_lang('AdditionalInfoRequired'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + } + + $serviceSaleId = $plugin->registerServiceSale( + $serviceId, + $formValues['payment_type'], + $infoSelected, + $formValues['c'] + ); + + if ($serviceSaleId !== false) { + $_SESSION['bc_service_sale_id'] = $serviceSaleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponServiceSale($serviceSaleId, $formValues['c']); + if ($couponSaleId !== false) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_process_confirm.php'); + } + exit; +} + +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + $coupon = $plugin->getCouponServiceByCode($formCouponValues['coupon_code'], $formCouponValues['i']); + + if ($coupon == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&c='.$formCouponValues['coupon_code']); + + exit; +} +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', intval($_GET['t'])); +$formCoupon->addHidden('i', intval($_GET['i'])); +if ($coupon != null) { + $form->addHidden('c', intval($coupon['id'])); +} +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); + +// View +$templateName = $plugin->get_lang('PaymentMethods'); +$interbreadcrumb[] = [ + 'url' => 'service_catalog.php', + 'name' => $plugin->get_lang('ListOfServicesOnSale'), +]; + +$tpl = new Template($templateName); +$tpl->assign('buying_service', true); +$tpl->assign('service', $serviceInfo); +$tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); +$tpl->assign('form', $form->returnForm()); +$content = $tpl->fetch('buycourses/view/service_process.tpl'); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/service_process_confirm.php b/plugin/buycourses/src/service_process_confirm.php new file mode 100644 index 000000000..6362b1f56 --- /dev/null +++ b/plugin/buycourses/src/service_process_confirm.php @@ -0,0 +1,385 @@ +getServiceSale($serviceSaleId, $coupon); +$userInfo = api_get_user_info($serviceSale['buyer']['id']); + +if (!empty($couponId)) { + $coupon = $plugin->getCouponService($couponId, $serviceSale['service_id']); + $serviceSale['item'] = $plugin->getService($serviceSale['service_id'], $coupon); +} + +if (empty($serviceSale)) { + api_not_allowed(true); +} + +$currency = $plugin->getCurrency($serviceSale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +switch ($serviceSale['payment_type']) { + case BuyCoursesPlugin::PAYMENT_TYPE_PAYPAL: + $paypalParams = $plugin->getPaypalParams(); + + $pruebas = $paypalParams['sandbox'] == 1; + $paypalUsername = $paypalParams['username']; + $paypalPassword = $paypalParams['password']; + $paypalSignature = $paypalParams['signature']; + + // This var $itemPrice may be "0" if the transaction does not include a one-time purchase such as when you set up + // a billing agreement for a recurring payment that is not immediately charged. When the field is set to 0, + // purchase-specific fields are ignored. This little condition handle this fact. + $itemPrice = $serviceSale['price']; + + $returnUrl = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_success.php'; + $cancelUrl = api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_error.php'; + + // The extra params for handle the hard job, this var is VERY IMPORTANT !! + $extra = ''; + require_once 'paypalfunctions.php'; + + $extra .= "&L_PAYMENTREQUEST_0_NAME0={$serviceSale['service']['name']}"; + $extra .= "&L_PAYMENTREQUEST_0_QTY0=1"; + $extra .= "&L_PAYMENTREQUEST_0_AMT0=$itemPrice"; + + // Full Checkout express + $expressCheckout = CallShortcutExpressCheckout( + $itemPrice, + $currency['iso_code'], + 'paypal', + $returnUrl, + $cancelUrl, + $extra + ); + + if ($expressCheckout['ACK'] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $expressCheckout['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + + $plugin->cancelServiceSale($serviceSale['id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; + } + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $serviceSale['buy_date'], + 'product' => $serviceSale['service']['name'], + 'currency' => $currency['iso_code'], + 'price' => $serviceSale['price'], + 'reference' => $serviceSale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + RedirectToPayPal($expressCheckout['TOKEN']); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_TRANSFER: + $transferAccounts = $plugin->getTransferAccounts(); + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelServiceSale($serviceSale['id']); + + unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_coupon_id']); + Display::addFlash( + Display::return_message($plugin->get_lang('OrderCancelled'), 'error', false) + ); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; + } + + $messageTemplate = new Template(); + $messageTemplate->assign( + 'service_sale', + [ + 'name' => $serviceSale['service']['name'], + 'buyer' => $serviceSale['buyer']['name'], + 'buy_date' => $serviceSale['buy_date'], + 'start_date' => $serviceSale['start_date'], + 'end_date' => $serviceSale['end_date'], + 'currency' => $currency['iso_code'], + 'price' => $serviceSale['price'], + 'reference' => $serviceSale['reference'], + ] + ); + $messageTemplate->assign('transfer_accounts', $transferAccounts); + $buyer = api_get_user_info($serviceSale['buyer']['id']); + + MessageManager::send_message_simple( + $buyer['user_id'], + $plugin->get_lang('bc_subject'), + $messageTemplate->fetch('buycourses/view/service_message_transfer.tpl') + ); + + if (!empty($globalParameters['sale_email'])) { + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $serviceSale['buy_date'], + 'product' => $serviceSale['service']['name'], + 'currency' => $currency['iso_code'], + 'price' => $serviceSale['price'], + 'reference' => $serviceSale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); + } + + Display::addFlash( + Display::return_message( + sprintf( + $plugin->get_lang('PurchaseStatusX'), + $plugin->get_lang('PendingReasonByTransfer') + ), + 'success', + false + ) + ); + + unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_coupon_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButton( + 'cancel', + $plugin->get_lang('CancelOrder'), + 'times', + 'danger', + 'default', + null, + ['id' => 'cancel'] + ); + + $template = new Template(); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $serviceSale['service']['name']); + $template->assign('price', $serviceSale['price']); + $template->assign('currency', $serviceSale['currency_id']); + $template->assign('buying_service', $serviceSale); + $template->assign('user', $userInfo); + $template->assign('service', $serviceSale['service']); + $template->assign('service_item', $serviceSale['item']); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + break; + case BuyCoursesPlugin::PAYMENT_TYPE_CULQI: + // We need to include the main online script, acording to the Culqi documentation the JS needs to be loeaded + // directly from the main url "https://integ-pago.culqi.com" because a local copy of this JS is not supported + $htmlHeadXtra[] = ''; + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + if (isset($formValues['cancel'])) { + $plugin->cancelServiceSale($serviceSale['id']); + + unset($_SESSION['bc_service_sale_id']); + unset($_SESSION['bc_coupon_id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning', + false + ) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + } + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButton( + 'cancel', + $plugin->get_lang('CancelOrder'), + 'times', + 'danger', + 'default', + null, + ['id' => 'cancel'] + ); + + $template = new Template(); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $serviceSale['service']['name']); + $template->assign('price', floatval($serviceSale['price'])); + $template->assign('currency', $plugin->getSelectedCurrency()); + $template->assign('buying_service', $serviceSale); + $template->assign('user', $userInfo); + $template->assign('service', $serviceSale['service']); + $template->assign('service_item', $serviceSale['item']); + $template->assign('form', $form->returnForm()); + $template->assign('is_culqi_payment', true); + $template->assign('culqi_params', $culqiParams = $plugin->getCulqiParams()); + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + $template->assign('content', $content); + $template->display_one_col_template(); + break; + + case BuyCoursesPlugin::PAYMENT_TYPE_TPV_CECABANK: + $cecabankParams = $plugin->getcecabankParams(); + $currency = $plugin->getCurrency($sale['currency_id']); + + $form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE + ); + + if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (isset($formValues['cancel'])) { + $plugin->cancelServiceSale($sale['id']); + + unset($_SESSION['bc_sale_id']); + unset($_SESSION['bc_coupon_id']); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $urlTpv = $cecabankParams['merchart_id']; + $currency = $plugin->getCurrency($sale['currency_id']); + $signature = $plugin->getCecabankSignature($sale['reference'], $sale['price']); + + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
'; + + echo ''; + + exit; + } + + $form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success', + 'default', + null, + ['id' => 'confirm'] + ); + $form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + + $template = new Template(); + $template->assign('terms', $globalParameters['terms_and_conditions']); + $template->assign('title', $serviceSale['service']['name']); + $template->assign('price', $serviceSale['price']); + $template->assign('currency', $serviceSale['currency_id']); + $template->assign('buying_service', $serviceSale); + $template->assign('user', $userInfo); + $template->assign('service', $serviceSale['service']); + $template->assign('service_item', $serviceSale['item']); + $template->assign('transfer_accounts', $transferAccounts); + $template->assign('form', $form->returnForm()); + + $content = $template->fetch('buycourses/view/process_confirm.tpl'); + + $template->assign('content', $content); + $template->display_one_col_template(); + + break; +} diff --git a/plugin/buycourses/src/service_sales_report.php b/plugin/buycourses/src/service_sales_report.php new file mode 100644 index 000000000..3d4cc6eff --- /dev/null +++ b/plugin/buycourses/src/service_sales_report.php @@ -0,0 +1,103 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); +$includeServices = $plugin->get('include_services'); +$invoicingEnable = $plugin->get('invoicing_enable') === 'true'; + +$saleStatuses = $plugin->getServiceSaleStatuses(); +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_PENDING; +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedStatus = $form->getSubmitValue('status'); + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::SALE_STATUS_PENDING; + } +} + +$form->addSelect('status', $plugin->get_lang('OrderStatus'), $saleStatuses, ['cols-size' => [0, 0, 0]]); +$form->addText('user', get_lang('User'), false, ['cols-size' => [0, 0, 0]]); +$form->addButtonSearch(get_lang('Search'), 'search'); + +$servicesSales = $plugin->getServiceSales(0, $selectedStatus); + +foreach ($servicesSales as &$sale) { + if (isset($sale['discount_amount']) && $sale['discount_amount'] != 0) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getServiceSaleCouponCode($sale['id']); + } +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$webPluginPath = api_get_path(WEB_PLUGIN_PATH); +$htmlHeadXtra[] = api_get_css($webPluginPath.'buycourses/resources/css/style.css'); +$htmlHeadXtra[] = api_get_css($webPluginPath.'buycourses/resources/js/modals.js'); + +$templateName = $plugin->get_lang('SalesReport'); + +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/export_report.php', + ['class' => 'btn btn-primary'] +); + +if ($paypalEnable == 'true' && $commissionsEnable == 'true') { + $toolbar .= Display::toolbarButton( + $plugin->get_lang('PaypalPayoutCommissions'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/paypal_payout.php', + 'paypal', + 'primary', + ['title' => $plugin->get_lang('PaypalPayoutCommissions')] + ); +} + +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +if ($commissionsEnable == 'true') { + $toolbar = Display::toolbarButton( + $plugin->get_lang('PayoutReport'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/payout_report.php', + 'money', + 'info', + ['title' => $plugin->get_lang('PayoutReport')] + ); + + $template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) + ); +} +$template->assign('form', $form->returnForm()); +$template->assign('showing_services', true); +$template->assign('services_are_included', $includeServices); +$template->assign('sale_list', $servicesSales); +$template->assign('sale_status_cancelled', BuyCoursesPlugin::SERVICE_STATUS_CANCELLED); +$template->assign('sale_status_pending', BuyCoursesPlugin::SERVICE_STATUS_PENDING); +$template->assign('sale_status_completed', BuyCoursesPlugin::SERVICE_STATUS_COMPLETED); +$template->assign('invoicing_enable', $invoicingEnable); +$content = $template->fetch('buycourses/view/service_sales_report.tpl'); +$template->assign('content', $content); +$template->assign('header', $templateName); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/service_success.php b/plugin/buycourses/src/service_success.php new file mode 100644 index 000000000..5dc02762e --- /dev/null +++ b/plugin/buycourses/src/service_success.php @@ -0,0 +1,186 @@ +get('paypal_enable') === 'true'; + +if (!$paypalEnabled) { + api_not_allowed(true); +} + +$serviceSaleId = $_SESSION['bc_service_sale_id']; +$serviceSale = $plugin->getServiceSale($serviceSaleId); +$itemPrice = $serviceSale['price']; + +if (empty($serviceSale)) { + api_not_allowed(true); +} + +$paypalParams = $plugin->getPaypalParams(); + +$pruebas = $paypalParams['sandbox'] == 1; +$paypalUsername = $paypalParams['username']; +$paypalPassword = $paypalParams['password']; +$paypalSignature = $paypalParams['signature']; + +require_once "paypalfunctions.php"; + +$buyerInformation = GetShippingDetails(urlencode($_SESSION['TOKEN'])); + +$form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE +); +$form->addButton( + 'confirm', + $plugin->get_lang('ConfirmOrder'), + 'check', + 'success' +); +$form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + if (isset($formValues['cancel'])) { + $plugin->cancelServiceSale($serviceSale['id']); + + unset($_SESSION['bc_service_sale_id']); + + Display::addFlash( + Display::return_message($plugin->get_lang('OrderCancelled'), 'error', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; + } + + $confirmPayments = ConfirmPayment($itemPrice); + if ($confirmPayments['ACK'] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $confirmPayments['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + unset($_SESSION['wizard']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; + } + + switch ($confirmPayments["PAYMENTINFO_0_PAYMENTSTATUS"]) { + case 'Completed': + $serviceSaleIsCompleted = $plugin->completeServiceSale($serviceSale['id']); + + if ($serviceSaleIsCompleted) { + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('SubscriptionToServiceXSuccessful'), $serviceSale['service']['name']), + 'success' + ) + ); + + break; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') + ); + break; + case 'Pending': + switch ($confirmPayments["PAYMENTINFO_0_PENDINGREASON"]) { + case 'address': + $purchaseStatus = $plugin->get_lang('PendingReasonByAddress'); + break; + case 'authorization': + $purchaseStatus = $plugin->get_lang('PendingReasonByAuthorization'); + break; + case 'echeck': + $purchaseStatus = $plugin->get_lang('PendingReasonByEcheck'); + break; + case 'intl': + $purchaseStatus = $plugin->get_lang('PendingReasonByIntl'); + break; + case 'multicurrency': + $purchaseStatus = $plugin->get_lang('PendingReasonByMulticurrency'); + break; + case 'order': + $purchaseStatus = $plugin->get_lang('PendingReasonByOrder'); + break; + case 'paymentreview': + $purchaseStatus = $plugin->get_lang('PendingReasonByPaymentReview'); + break; + case 'regulatoryreview': + $purchaseStatus = $plugin->get_lang('PendingReasonByRegulatoryReview'); + break; + case 'unilateral': + $purchaseStatus = $plugin->get_lang('PendingReasonByUnilateral'); + break; + case 'upgrade': + $purchaseStatus = $plugin->get_lang('PendingReasonByUpgrade'); + break; + case 'verify': + $purchaseStatus = $plugin->get_lang('PendingReasonByVerify'); + break; + case 'other': + default: + $purchaseStatus = $plugin->get_lang('PendingReasonByOther'); + break; + } + + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('PurchaseStatusX'), $purchaseStatus), + 'warning', + false + ) + ); + break; + default: + $plugin->cancelServiceSale(intval($serviceSale['id'])); + + Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') + ); + break; + } + + unset($_SESSION['bc_service_sale_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/service_catalog.php'); + exit; +} + +$token = isset($_GET['token']) ? Security::remove_XSS($_GET['token']) : null; +if (empty($token)) { + api_not_allowed(true); +} + +$interbreadcrumb[] = [ + "url" => "service_catalog.php", + "name" => $plugin->get_lang('ListOfServicesOnSale'), +]; + +$templateName = $plugin->get_lang('PaymentMethods'); +$tpl = new Template($templateName); +$tpl->assign('title', $serviceSale['service']['name']); +$tpl->assign('price', $serviceSale['price']); +$tpl->assign('currency', $serviceSale['currency_id']); +$tpl->assign('service', $serviceSale); +$tpl->assign('buying_service', true); +$tpl->assign('user', api_get_user_info($serviceSale['buyer']['id'])); +$tpl->assign('form', $form->returnForm()); + +$content = $tpl->fetch('buycourses/view/success.tpl'); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/services_add.php b/plugin/buycourses/src/services_add.php new file mode 100644 index 000000000..0d038b614 --- /dev/null +++ b/plugin/buycourses/src/services_add.php @@ -0,0 +1,141 @@ +getSelectedCurrency(); +$em = Database::getManager(); +$users = UserManager::getRepository()->findAll(); +$userOptions = []; +if (!empty($users)) { + foreach ($users as $user) { + $userOptions[$user->getId()] = $user->getCompleteNameWithUsername(); + } +} + +api_protect_admin_script(true); + +$htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css'); +$htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js'); + +//view +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; +$interbreadcrumb[] = [ + 'url' => 'list_service.php', + 'name' => $plugin->get_lang('Services'), +]; + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$formDefaultValues = [ + 'price' => 0, + 'tax_perc' => $globalSettingsParams['global_tax_perc'], + 'duration_days' => 0, + 'applies_to' => 0, + 'visibility' => true, +]; + +$form = new FormValidator('Services'); +$form->addText('name', $plugin->get_lang('ServiceName')); +$form->addHtmlEditor('description', $plugin->get_lang('Description')); +$form->addElement( + 'number', + 'price', + [$plugin->get_lang('Price'), null, $currency['iso_code']], + ['step' => 0.01] +); +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); +$form->addElement( + 'number', + 'duration_days', + [$plugin->get_lang('Duration'), null, get_lang('Days')], + ['step' => 1] +); +$form->addElement( + 'radio', + 'applies_to', + $plugin->get_lang('AppliesTo'), + get_lang('None'), + 0 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('User'), + 1 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('Course'), + 2 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('Session'), + 3 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('TemplateTitleCertificate'), + 4 +); +$form->addSelect( + 'owner_id', + get_lang('Owner'), + $userOptions +); +$form->addCheckBox('visibility', $plugin->get_lang('VisibleInCatalog')); +$form->addFile( + 'picture', + (get_lang( + 'AddImage' + )), + ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '16 / 9'] +); +$form->addText('video_url', get_lang('VideoUrl'), false); +$form->addHtmlEditor('service_information', $plugin->get_lang('ServiceInformation'), false); +$form->addButtonSave(get_lang('Add')); +$form->setDefaults($formDefaultValues); + +if ($form->validate()) { + $values = $form->getSubmitValues(); + + $plugin->storeService($values); + + Display::addFlash( + Display::return_message($plugin->get_lang('ServiceAdded'), 'success') + ); + + header('Location: list_service.php'); + exit; +} + +$templateName = $plugin->get_lang('NewService'); +$tpl = new Template($templateName); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $form->returnForm()); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/services_edit.php b/plugin/buycourses/src/services_edit.php new file mode 100644 index 000000000..98d801866 --- /dev/null +++ b/plugin/buycourses/src/services_edit.php @@ -0,0 +1,161 @@ +getSelectedCurrency(); +$users = UserManager::getRepository()->findAll(); +$userOptions = []; +if (!empty($users)) { + foreach ($users as $user) { + $userOptions[$user->getId()] = $user->getCompleteNameWithUsername(); + } +} + +api_protect_admin_script(true); +$htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css'); +$htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js'); + +//view +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; +$interbreadcrumb[] = [ + 'url' => 'list_service.php', + 'name' => $plugin->get_lang('Services'), +]; + +$globalSettingsParams = $plugin->getGlobalParameters(); +$service = $plugin->getService($serviceId); + +$formDefaultValues = [ + 'name' => $service['name'], + 'description' => $service['description'], + 'price' => $service['price'], + 'tax_perc' => $service['tax_perc'], + 'duration_days' => $service['duration_days'], + 'owner_id' => intval($service['owner_id']), + 'applies_to' => intval($service['applies_to']), + 'visibility' => ($service['visibility'] == 1) ? true : false, + 'image' => is_file(api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$serviceId.'.png') + ? api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$serviceId.'.png' + : api_get_path(WEB_CODE_PATH).'img/session_default.png', + 'video_url' => $service['video_url'], + 'service_information' => $service['service_information'], +]; + +$form = new FormValidator('Services'); +$form->addText('name', $plugin->get_lang('ServiceName')); +$form->addHtmlEditor('description', $plugin->get_lang('Description')); +$form->addElement( + 'number', + 'price', + [$plugin->get_lang('Price'), null, $currency['iso_code']], + ['step' => 0.01] +); +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); +$form->addElement( + 'number', + 'duration_days', + [$plugin->get_lang('Duration'), null, get_lang('Days')], + ['step' => 1] +); +$form->addElement( + 'radio', + 'applies_to', + $plugin->get_lang('AppliesTo'), + get_lang('None'), + 0 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('User'), + 1 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('Course'), + 2 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('Session'), + 3 +); +$form->addElement( + 'radio', + 'applies_to', + null, + get_lang('TemplateTitleCertificate'), + 4 +); +$form->addSelect( + 'owner_id', + get_lang('Owner'), + $userOptions +); +$form->addCheckBox('visibility', $plugin->get_lang('VisibleInCatalog')); +$form->addFile( + 'picture', + $formDefaultValues['image'] != '' ? get_lang('UpdateImage') : get_lang('AddImage'), + ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '16 / 9'] +); +$form->addText('video_url', get_lang('VideoUrl'), false); +$form->addHtmlEditor('service_information', $plugin->get_lang('ServiceInformation'), false); +$form->addHidden('id', $serviceId); +$form->addButtonSave(get_lang('Edit')); +$form->addHtml('



'); +$form->addButtonDelete($plugin->get_lang('DeleteThisService'), 'delete_service'); +$form->setDefaults($formDefaultValues); +if ($form->validate()) { + $values = $form->getSubmitValues(); + + if (isset($values['delete_service'])) { + $plugin->deleteService($serviceId); + Display::addFlash( + Display::return_message($plugin->get_lang('ServiceDeleted'), 'error') + ); + } else { + $plugin->updateService($values, $serviceId); + Display::addFlash( + Display::return_message($plugin->get_lang('ServiceEdited'), 'success') + ); + } + header('Location: list_service.php'); + exit; +} + +$templateName = $plugin->get_lang('EditService'); +$tpl = new Template($templateName); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $form->returnForm()); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/session_catalog.php b/plugin/buycourses/src/session_catalog.php new file mode 100644 index 000000000..47ea13108 --- /dev/null +++ b/plugin/buycourses/src/session_catalog.php @@ -0,0 +1,127 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +if (!$includeSessions) { + api_not_allowed(true); +} + +$nameFilter = null; +$minFilter = 0; +$maxFilter = 0; +$sessionCategory = isset($_GET['session_category']) ? (int) $_GET['session_category'] : 0; +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +$form->removeAttribute('class'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $minFilter = isset($formValues['min']) ? $formValues['min'] : 0; + $maxFilter = isset($formValues['max']) ? $formValues['max'] : 0; + $sessionCategory = isset($formValues['session_category']) ? $formValues['session_category'] : $sessionCategory; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); + +$categoriesOptions = [ + '0' => get_lang('AllCategories'), +]; +$categoriesList = SessionManager::get_all_session_category(); +if ($categoriesList != false) { + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem['id']] = $categoryItem['name']; + } +} +$form->addSelect( + 'session_category', + get_lang('SessionCategory'), + $categoriesOptions, + [ + 'id' => 'session_category', + ] +); + +$form->addText('name', get_lang('SessionName'), false); + +$form->addElement( + 'number', + 'min', + $plugin->get_lang('MinimumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$form->addElement( + 'number', + 'max', + $plugin->get_lang('MaximumPrice'), + ['step' => '0.01', 'min' => '0'] +); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$form->setDefaults( + [ + 'session_category' => $sessionCategory, + ] +); +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$sessionList = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'all', $sessionCategory); +$totalItems = $plugin->getCatalogSessionList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count', $sessionCategory); +$pagesCount = ceil($totalItems / $pageSize); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'list.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} + +$templateName = $plugin->get_lang('CourseListOnSale'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$template = new Template($templateName); +$template->assign('search_filter_form', $form->returnForm()); +$template->assign('sessions_are_included', $includeSessions); +$template->assign('services_are_included', $includeServices); +$template->assign('showing_sessions', true); +$template->assign('sessions', $sessionList); +$template->assign('pagination', $pagination); + +$countCourses = $plugin->getCatalogCourseList($first, $pageSize, $nameFilter, $minFilter, $maxFilter, 'count'); + +$template->assign('coursesExist', $countCourses > 0); +$template->assign('sessionExist', true); + +$content = $template->fetch('buycourses/view/catalog.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/session_panel.php b/plugin/buycourses/src/session_panel.php new file mode 100644 index 000000000..f6506a3fa --- /dev/null +++ b/plugin/buycourses/src/session_panel.php @@ -0,0 +1,67 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$userInfo = api_get_user_info(); + +$productTypes = $plugin->getProductTypes(); +$saleStatuses = $plugin->getSaleStatuses(); +$paymentTypes = $plugin->getPaymentTypes(); + +$sales = $plugin->getSaleListByUserId($userInfo['id']); + +$saleList = []; + +foreach ($sales as $sale) { + if ($sale['product_type'] == 2) { + $saleList[] = [ + 'id' => $sale['id'], + 'reference' => $sale['reference'], + 'date' => api_format_date($sale['date'], DATE_TIME_FORMAT_LONG_24H), + 'currency' => $sale['iso_code'], + 'price' => $sale['price'], + 'product_name' => $sale['product_name'], + 'product_type' => $productTypes[$sale['product_type']], + 'payment_type' => $paymentTypes[$sale['payment_type']], + ]; + } +} + +$toolbar = Display::toolbarButton( + $plugin->get_lang('CourseListOnSale'), + 'course_catalog.php', + 'search-plus', + 'primary', + ['title' => $plugin->get_lang('CourseListOnSale')] +); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = get_lang('TabsDashboard'); +$tpl = new Template($templateName); +$tpl->assign('showing_courses', true); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('sale_list', $saleList); + +$content = $tpl->fetch('buycourses/view/session_panel.tpl'); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/stripe_cancel.php b/plugin/buycourses/src/stripe_cancel.php new file mode 100644 index 000000000..1382ec627 --- /dev/null +++ b/plugin/buycourses/src/stripe_cancel.php @@ -0,0 +1,30 @@ +get('stripe_enable') === 'true'; + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/src/stripe_response.php b/plugin/buycourses/src/stripe_response.php new file mode 100644 index 000000000..f4b11bd88 --- /dev/null +++ b/plugin/buycourses/src/stripe_response.php @@ -0,0 +1,65 @@ +get('stripe_enable') === 'true'; + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$stripeParams = $plugin->getStripeParams(); + +$payload = @file_get_contents('php://input'); +$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; +$event = null; + +try { + $event = \Stripe\Webhook::constructEvent( + $payload, $sig_header, $stripeParams['endpoint_secret'] + ); +} catch (\UnexpectedValueException $e) { + http_response_code(400); + exit(); +} catch (\Stripe\Exception\SignatureVerificationException $e) { + http_response_code(400); + exit(); +} + +switch ($event->type) { + case 'checkout.session.completed': + $checkoutSession = $event->data->object; + + $sale = $plugin->getSaleFromReference($checkoutSession->id); + + if (empty($sale)) { + api_not_allowed(true); + } + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + break; + } + + $saleIsCompleted = $plugin->completeSale($sale['id']); + + if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); + } + + // no break + default: + echo 'Received unknown event type '.$event->type; +} + +http_response_code(200); diff --git a/plugin/buycourses/src/stripe_success.php b/plugin/buycourses/src/stripe_success.php new file mode 100644 index 000000000..545e3fcb3 --- /dev/null +++ b/plugin/buycourses/src/stripe_success.php @@ -0,0 +1,78 @@ +get('stripe_enable') === 'true'; + +if (!$stripeEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$userInfo = api_get_user_info($sale['user_id']); +$currency = $plugin->getCurrency($sale['currency_id']); +$globalParameters = $plugin->getGlobalParameters(); + +if (!empty($globalParameters['sale_email'])) { + $messageConfirmBuyerTemplate = new Template(); + $messageConfirmBuyerTemplate->assign('user', $userInfo); + $messageConfirmBuyerTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + $userInfo['complete_name'], + $userInfo['email'], + $plugin->get_lang('bc_subject'), + $messageConfirmBuyerTemplate->fetch('buycourses/view/message_confirm_buyer.tpl'), + '', + $globalParameters['sale_email'] + ); + + $messageConfirmTemplate = new Template(); + $messageConfirmTemplate->assign('user', $userInfo); + $messageConfirmTemplate->assign( + 'sale', + [ + 'date' => $sale['date'], + 'product' => $sale['product_name'], + 'currency' => $currency['iso_code'], + 'price' => $sale['price'], + 'reference' => $sale['reference'], + ] + ); + + api_mail_html( + '', + $globalParameters['sale_email'], + $plugin->get_lang('bc_subject'), + $messageConfirmTemplate->fetch('buycourses/view/message_confirm.tpl') + ); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/src/subscription_add.php b/plugin/buycourses/src/subscription_add.php new file mode 100644 index 000000000..3ae12ba45 --- /dev/null +++ b/plugin/buycourses/src/subscription_add.php @@ -0,0 +1,234 @@ +get('include_sessions') === 'true'; + +$entityManager = Database::getManager(); +$userRepo = UserManager::getRepository(); +$currency = $plugin->getSelectedCurrency(); + +if (empty($currency)) { + Display::addFlash( + Display::return_message($plugin->get_lang('CurrencyIsNotConfigured'), 'error') + ); +} + +$currencyIso = null; + +if ($editingCourse) { + $course = $entityManager->find('ChamiloCoreBundle:Course', $id); + if (!$course) { + api_not_allowed(true); + } + + $courseItem = $plugin->getCourseForConfiguration($course, $currency); + + $currencyIso = $courseItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Course'), + 'id' => $courseItem['course_id'], + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_COURSE, + 'name' => $courseItem['course_title'], + 'visible' => $courseItem['visible'], + 'price' => $courseItem['price'], + 'tax_perc' => $courseItem['tax_perc'], + 'currency_id' => $currency['id'], + ]; +} elseif ($editingSession) { + if (!$includeSession) { + api_not_allowed(true); + } + + $session = $entityManager->find('ChamiloCoreBundle:Session', $id); + if (!$session) { + api_not_allowed(true); + } + + $sessionItem = $plugin->getSessionForConfiguration($session, $currency); + + $currencyIso = $sessionItem['currency']; + $formDefaults = [ + 'product_type' => get_lang('Session'), + 'id' => $session->getId(), + 'type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION, + 'name' => $sessionItem['session_name'], + 'visible' => $sessionItem['visible'], + 'price' => $sessionItem['price'], + 'tax_perc' => $sessionItem['tax_perc'], + 'currency_id' => $currency['id'], + ]; +} else { + api_not_allowed(true); +} + +$globalSettingsParams = $plugin->getGlobalParameters(); + +$form = new FormValidator('add_subscription'); + +$form->addText('product_type', $plugin->get_lang('ProductType'), false); +$form->addText('name', get_lang('Name'), false); + +$form->freeze(['product_type', 'name']); + +$form->addElement( + 'number', + 'tax_perc', + [$plugin->get_lang('TaxPerc'), $plugin->get_lang('TaxPercDescription'), '%'], + ['step' => 1, 'placeholder' => $globalSettingsParams['global_tax_perc'].'% '.$plugin->get_lang('ByDefault')] +); + +$frequencies = $plugin->getFrequencies(); + +$selectOptions = ''; +foreach ($frequencies as $key => $frequency) { + $selectOptions .= ''; +} + +if (empty($frequencies)) { + Display::addFlash( + Display::return_message($plugin->get_lang('FrequencyIsNotConfigured'), 'error') + ); +} + +$platformCommission = $plugin->getPlatformCommission(); +$form->addHtml( + ' +
+
+
+
+

'.$plugin->get_lang('FrequencyConfig').'

+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+ '.$currencyIso.' +
+
+
+
+ Add +
+
+
+
+
+
+ + + + + + + + + + +
'.$plugin->get_lang('Duration').''.$plugin->get_lang('Price').''.$plugin->get_lang('Actions').'
+
+
+
+
+
+
+
+
+
+ ' +); + +$form->addHidden('type', null); +$form->addHidden('id', null); +$button = $form->addButtonSave(get_lang('Save')); + +if (empty($currency) || empty($frequencies)) { + $button->setAttribute('disabled'); +} + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $subscription['product_id'] = $formValues['id']; + $subscription['product_type'] = $formValues['type']; + $subscription['currency_id'] = $currency['id']; + $subscription['tax_perc'] = $formValues['tax_perc'] != '' ? (int) $formValues['tax_perc'] : null; + $subscription['frequencies'] = isset($formValues['frequencies']) ? $formValues['frequencies'] : []; + + $result = $plugin->addNewSubscription($subscription); + + if ($result) { + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscriptions_courses.php'); + } else { + header('Location:'.api_get_self().'?'.$queryString); + } + + exit; +} + +$form->setDefaults($formDefaults); + +$templateName = $plugin->get_lang('SubscriptionAdd'); +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => get_lang('Configuration'), +]; +$interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('SubscriptionList'), +]; + +$template = new Template($templateName); +$template->assign('header', $templateName); +$template->assign('items_form', $form->returnForm()); +$template->assign('currencyIso', $currencyIso); + +$content = $template->fetch('buycourses/view/subscription_add.tpl'); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/subscription_course_catalog.php b/plugin/buycourses/src/subscription_course_catalog.php new file mode 100644 index 000000000..db9e2c7f7 --- /dev/null +++ b/plugin/buycourses/src/subscription_course_catalog.php @@ -0,0 +1,88 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +$nameFilter = ''; + +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); +$form->addText('name', get_lang('CourseName'), false); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$courseList = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter); +$totalItems = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter, 'count'); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'subscriptions_courses.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} else { + $interbreadcrumb[] = [ + 'url' => 'course_panel.php', + 'name' => get_lang('TabsDashboard'), + ]; +} + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('SubscriptionListOnSale'); +$tpl = new Template($templateName); +$tpl->assign('search_filter_form', $form->returnForm()); +$tpl->assign('showing_courses', true); +$tpl->assign('courses', $courseList); +$tpl->assign('sessions_are_included', $includeSessions); +$tpl->assign('pagination', $pagination); + +$sessionList = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'first', 0); +$coursesExist = true; +$sessionExist = true; +if (count($sessionList) <= 0) { + $sessionExist = false; +} + +$tpl->assign('coursesExist', $coursesExist); +$tpl->assign('sessionExist', $sessionExist); + +$content = $tpl->fetch('buycourses/view/subscription_catalog.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/subscription_process.php b/plugin/buycourses/src/subscription_process.php new file mode 100644 index 000000000..ab5034247 --- /dev/null +++ b/plugin/buycourses/src/subscription_process.php @@ -0,0 +1,266 @@ +'; +$plugin = BuyCoursesPlugin::create(); +$includeSession = $plugin->get('include_sessions') === 'true'; +$paypalEnabled = $plugin->get('paypal_enable') === 'true'; +$transferEnabled = $plugin->get('transfer_enable') === 'true'; +$culqiEnabled = $plugin->get('culqi_enable') === 'true'; +$tpvRedsysEnable = $plugin->get('tpv_redsys_enable') === 'true'; + +if (!$paypalEnabled && !$transferEnabled && !$culqiEnabled && !$tpvRedsysEnable) { + api_not_allowed(true); +} + +if (!isset($_REQUEST['t'], $_REQUEST['i'])) { + api_not_allowed(true); +} + +$buyingCourse = intval($_REQUEST['t']) === BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$buyingSession = intval($_REQUEST['t']) === BuyCoursesPlugin::PRODUCT_TYPE_SESSION; +$queryString = 'i='.intval($_REQUEST['i']).'&t='.intval($_REQUEST['t']); + +if (isset($_REQUEST['c'])) { + $couponCode = $_REQUEST['c']; + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($couponCode, BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($couponCode, BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + $queryString .= 'c='.$coupon['code']; +} + +if (isset($_REQUEST['d'])) { + $duration = $_REQUEST['d']; + if ($buyingCourse) { + $subscriptionItem = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i'], $duration, $coupon); + } else { + $subscriptionItem = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i'], $duration, $coupon); + } +} + +if (empty($currentUserId)) { + Session::write('buy_course_redirect', api_get_self().'?'.$queryString); + header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php'); + exit; +} + +$subscriptionItems = $plugin->getSubscriptionsItemsByProduct($_REQUEST['i'], $_REQUEST['t']); + +if (!isset($subscriptionItems) || empty($subscriptionItems)) { + api_not_allowed(true); +} + +if (!isset($subscriptionItem) || empty($subscriptionItem)) { + $subscriptionItem = $plugin->getSubscription($subscriptionItems[0]['product_type'], $subscriptionItems[0]['product_id'], $subscriptionItems[0]['duration'], $coupon); +} + +$queryString .= 'd='.intval($subscriptionItem['duration']); + +if ($buyingCourse) { + $courseInfo = $plugin->getSubscriptionCourseInfo($_REQUEST['i'], $coupon); + $item = $plugin->getSubscriptionItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +} elseif ($buyingSession) { + $sessionInfo = $plugin->getSubscriptionSessionInfo($_REQUEST['i'], $coupon); + $item = $plugin->getSubscriptionItemByProduct($_REQUEST['i'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +} + +$form = new FormValidator('confirm_sale'); +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + + if (!$formValues['payment_type']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToSelectPaymentType'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + $saleId = $plugin->registerSubscriptionSale($item['product_id'], $item['product_type'], $formValues['payment_type'], $formValues['d'], $formValues['c']); + + if ($saleId !== false) { + $_SESSION['bc_sale_id'] = $saleId; + + if (isset($formValues['c'])) { + $couponSaleId = $plugin->registerCouponSubscriptionSale($saleId, $formValues['c']); + if ($couponSaleId !== false) { + $plugin->updateCouponDelivered($formValues['c']); + $_SESSION['bc_coupon_id'] = $formValues['c']; + } + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/process_subscription_confirm.php'); + } + + exit; +} + +$paymentTypesOptions = $plugin->getPaymentTypes(true); + +$count = count($paymentTypesOptions); +if ($count === 0) { + $form->addHtml($plugin->get_lang('NoPaymentOptionAvailable')); + $form->addHtml('
'); + $form->addHtml('
'); +} elseif ($count === 1) { + // get the only array item + foreach ($paymentTypesOptions as $type => $value) { + $form->addHtml(sprintf($plugin->get_lang('XIsOnlyPaymentMethodAvailable'), $value)); + $form->addHtml('
'); + $form->addHtml('
'); + $form->addHidden('payment_type', $type); + } +} else { + $form->addHtml( + Display::return_message( + $plugin->get_lang('PleaseSelectThePaymentMethodBeforeConfirmYourOrder'), + 'info' + ) + ); + $form->addRadio('payment_type', null, $paymentTypesOptions); +} + +$form->addHidden('t', intval($_GET['t'])); +$form->addHidden('i', intval($_GET['i'])); +if ($coupon != null) { + $form->addHidden('c', intval($coupon['id'])); +} +$form->addButton('submit', $plugin->get_lang('ConfirmOrder'), 'check', 'success', 'btn-lg pull-right'); + +$formSubscription = new FormValidator('confirm_subscription'); +if ($formSubscription->validate()) { + $formSubscriptionValues = $formSubscription->getSubmitValues(); + + if (!$formSubscriptionValues['duration']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddDuration'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + if ($buyingCourse) { + $subscription = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i'], $formSubscriptionValues['duration']); + } else { + $subscription = $plugin->getSubscription(BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i'], $formSubscriptionValues['duration']); + } + + if ($subscription == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('SubscriptionNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscription_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&d='.$formSubscriptionValues['duration']); + + exit; +} + +$frequencies = $plugin->getFrequencies(); +$selectedFrequencies = []; + +foreach ($subscriptionItems as $si) { + if (isset($frequencies[$si['duration']])) { + $selectedFrequencies[$si['duration']] = $frequencies[$si['duration']].' - '.$si['price_formatted'].' '.$si['iso_code']; + } +} + +$formSubscription->addRadio('duration', null, $selectedFrequencies); + +if (!empty($selectedFrequencies)) { + $formSubscriptionDefaults['duration'] = $subscriptionItem['duration']; + $formSubscription->setDefaults($formSubscriptionDefaults); +} + +$selectedDurationName = $frequencies[$subscriptionItem['duration']]; + +$formSubscription->addHidden('t', intval($_GET['t'])); +$formSubscription->addHidden('i', intval($_GET['i'])); + +$form->addHidden('d', $subscriptionItem['duration']); + +$formCoupon = new FormValidator('confirm_coupon'); +if ($formCoupon->validate()) { + $formCouponValues = $formCoupon->getSubmitValues(); + + if (!$formCouponValues['coupon_code']) { + Display::addFlash( + Display::return_message($plugin->get_lang('NeedToAddCouponCode'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + if ($buyingCourse) { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_COURSE, $_REQUEST['i']); + } else { + $coupon = $plugin->getCouponByCode($formCouponValues['coupon_code'], BuyCoursesPlugin::PRODUCT_TYPE_SESSION, $_REQUEST['i']); + } + + if ($coupon == null) { + Display::addFlash( + Display::return_message($plugin->get_lang('CouponNotValid'), 'error', false) + ); + header('Location:'.api_get_self().'?'.$queryString); + exit; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('CouponRedeemed'), 'success', false) + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/subscription_process.php?i='.$_REQUEST['i'].'&t='.$_REQUEST['t'].'&d='.$_REQUEST['d'].'&c='.$formCouponValues['coupon_code']); + + exit; +} +$formCoupon->addText('coupon_code', $plugin->get_lang('CouponsCode'), true); +$formCoupon->addHidden('t', intval($_GET['t'])); +$formCoupon->addHidden('i', intval($_GET['i'])); +$formCoupon->addHidden('d', $subscriptionItem['duration']); +$formCoupon->addButton('submit', $plugin->get_lang('RedeemCoupon'), 'check', 'success', 'btn-lg pull-right'); + +// View +$templateName = $plugin->get_lang('PaymentMethods'); +$interbreadcrumb[] = ['url' => 'subscription_course_catalog.php', 'name' => $plugin->get_lang('CourseListOnSale')]; + +$tpl = new Template($templateName); +$tpl->assign('item_type', (int) $_GET['t']); +$tpl->assign('buying_course', $buyingCourse); +$tpl->assign('buying_session', $buyingSession); +$tpl->assign('user', api_get_user_info()); +$tpl->assign('form_coupon', $formCoupon->returnForm()); +$tpl->assign('message_payment', $messagePayment); +$tpl->assign('selected_duration_name', $selectedDurationName); +$tpl->assign('form', $form->returnForm()); +$tpl->assign('form_subscription', $formSubscription->returnForm()); + +if ($buyingCourse) { + $tpl->assign('course', $courseInfo); +} elseif ($buyingSession) { + $tpl->assign('session', $sessionInfo); +} + +$tpl->assign('subscription', $subscriptionItem); + +$content = $tpl->fetch('buycourses/view/subscription_process.tpl'); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/subscription_sales_report.php b/plugin/buycourses/src/subscription_sales_report.php new file mode 100644 index 000000000..a29c4eded --- /dev/null +++ b/plugin/buycourses/src/subscription_sales_report.php @@ -0,0 +1,201 @@ +get('paypal_enable'); +$commissionsEnable = $plugin->get('commissions_enable'); +$includeServices = $plugin->get('include_services'); +$invoicingEnable = $plugin->get('invoicing_enable') === 'true'; + +if (isset($_GET['order'])) { + $sale = $plugin->getSubscriptionSale($_GET['order']); + + if (empty($sale)) { + api_not_allowed(true); + } + + $urlToRedirect = api_get_self().'?'; + + switch ($_GET['action']) { + case 'confirm': + $plugin->completeSubscriptionSale($sale['id']); + $plugin->storeSubscriptionPayouts($sale['id']); + Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_COMPLETED, + 'sale' => $sale['id'], + ]); + break; + case 'cancel': + $plugin->cancelSubscriptionSale($sale['id']); + + Display::addFlash( + Display::return_message( + $plugin->get_lang('OrderCanceled'), + 'warning' + ) + ); + + $urlToRedirect .= http_build_query([ + 'status' => BuyCoursesPlugin::SALE_STATUS_CANCELED, + 'sale' => $sale['id'], + ]); + break; + } + + header("Location: $urlToRedirect"); + exit; +} + +$productTypes = $plugin->getProductTypes(); +$saleStatuses = $plugin->getSaleStatuses(); +$paymentTypes = $plugin->getPaymentTypes(); + +$selectedFilterType = '0'; +$selectedStatus = isset($_GET['status']) ? $_GET['status'] : BuyCoursesPlugin::SALE_STATUS_PENDING; +$selectedSale = isset($_GET['sale']) ? intval($_GET['sale']) : 0; +$dateStart = isset($_GET['date_start']) ? $_GET['date_start'] : date('Y-m-d H:i', mktime(0, 0, 0)); +$dateEnd = isset($_GET['date_end']) ? $_GET['date_end'] : date('Y-m-d H:i', mktime(23, 59, 59)); +$searchTerm = ''; +$email = ''; + +$form = new FormValidator('search', 'get'); + +if ($form->validate()) { + $selectedFilterType = $form->getSubmitValue('filter_type'); + $selectedStatus = $form->getSubmitValue('status'); + $searchTerm = $form->getSubmitValue('user'); + $dateStart = $form->getSubmitValue('date_start'); + $dateEnd = $form->getSubmitValue('date_end'); + $email = $form->getSubmitValue('email'); + + if ($selectedStatus === false) { + $selectedStatus = BuyCoursesPlugin::SALE_STATUS_PENDING; + } + + if ($selectedFilterType === false) { + $selectedFilterType = '0'; + } +} + +$form->addRadio( + 'filter_type', + get_lang('Filter'), + [ + $plugin->get_lang('ByStatus'), + $plugin->get_lang('ByUser'), + $plugin->get_lang('ByDate'), + $plugin->get_lang('ByEmail'), + ] +); +$form->addHtml('
'); +$form->addSelect('status', $plugin->get_lang('OrderStatus'), $saleStatuses); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('user', get_lang('UserName'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addDateRangePicker('date', get_lang('Date'), false); +$form->addHtml('
'); +$form->addHtml('
'); +$form->addText('email', get_lang('Email'), false); +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); +$form->setDefaults([ + 'filter_type' => $selectedFilterType, + 'status' => $selectedStatus, + 'date_start' => $dateStart, + 'date_end' => $dateEnd, + 'email' => $email, +]); + +switch ($selectedFilterType) { + case '0': + $sales = $plugin->getSubscriptionSaleListByStatus($selectedStatus); + break; + case '1': + $sales = $plugin->getSubscriptionSaleListByUser($searchTerm); + break; + case '2': + $sales = $plugin->getSubscriptionSaleListByDate($dateStart, $dateEnd); + break; + case '3': + $sales = $plugin->getSubscriptionSaleListByEmail($email); + break; +} + +foreach ($sales as &$sale) { + $sale['product_type'] = $productTypes[$sale['product_type']]; + $sale['payment_type'] = $paymentTypes[$sale['payment_type']]; + $sale['complete_user_name'] = api_get_person_name($sale['firstname'], $sale['lastname']); + $sale['num_invoice'] = $plugin->getNumInvoice($sale['id'], 0); + $sale['total_price'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['price'], $sale['iso_code']); + if (isset($sale['discount_amount']) && $sale['discount_amount'] != 0) { + $sale['total_discount'] = $plugin->getPriceWithCurrencyFromIsoCode($sale['discount_amount'], $sale['iso_code']); + $sale['coupon_code'] = $plugin->getSaleCouponCode($sale['id']); + } +} + +$interbreadcrumb[] = ['url' => '../index.php', 'name' => $plugin->get_lang('plugin_title')]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('SalesReport'); +$template = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('file-excel-o'). + get_lang('GenerateReport'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/export_subscription_report.php', + ['class' => 'btn btn-primary'] +); + +if ($paypalEnable === 'true' && $commissionsEnable === 'true') { + $toolbar .= Display::toolbarButton( + $plugin->get_lang('PaypalPayoutCommissions'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/paypal_payout.php', + 'paypal', + 'primary', + ['title' => $plugin->get_lang('PaypalPayoutCommissions')] + ); + + $template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) + ); +} + +$template->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); +$template->assign('form', $form->returnForm()); +$template->assign('selected_sale', $selectedSale); +$template->assign('selected_status', $selectedStatus); +$template->assign('services_are_included', $includeServices); +$template->assign('sale_list', $sales); +$template->assign('sale_status_canceled', BuyCoursesPlugin::SALE_STATUS_CANCELED); +$template->assign('sale_status_pending', BuyCoursesPlugin::SALE_STATUS_PENDING); +$template->assign('sale_status_completed', BuyCoursesPlugin::SALE_STATUS_COMPLETED); +$template->assign('invoicing_enable', $invoicingEnable); + +$content = $template->fetch('buycourses/view/subscription_sales_report.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/subscription_session_catalog.php b/plugin/buycourses/src/subscription_session_catalog.php new file mode 100644 index 000000000..a52309899 --- /dev/null +++ b/plugin/buycourses/src/subscription_session_catalog.php @@ -0,0 +1,114 @@ +get('include_sessions') === 'true'; +$includeServices = $plugin->get('include_services') === 'true'; + +if (!$includeSessions) { + api_not_allowed(true); +} + +$nameFilter = null; +$sessionCategory = isset($_GET['session_category']) ? (int) $_GET['session_category'] : 0; +$form = new FormValidator( + 'search_filter_form', + 'get', + null, + null, + [], + FormValidator::LAYOUT_INLINE +); + +$form->removeAttribute('class'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + $nameFilter = isset($formValues['name']) ? $formValues['name'] : null; + $sessionCategory = isset($formValues['session_category']) ? $formValues['session_category'] : $sessionCategory; +} + +$form->addHeader($plugin->get_lang('SearchFilter')); + +$categoriesOptions = [ + '0' => get_lang('AllCategories'), +]; +$categoriesList = SessionManager::get_all_session_category(); +if ($categoriesList != false) { + foreach ($categoriesList as $categoryItem) { + $categoriesOptions[$categoryItem['id']] = $categoryItem['name']; + } +} +$form->addSelect( + 'session_category', + get_lang('SessionCategory'), + $categoriesOptions, + [ + 'id' => 'session_category', + ] +); + +$form->addText('name', get_lang('SessionName'), false); + +$form->addHtml('
'); +$form->addButtonFilter(get_lang('Search')); + +$form->setDefaults( + [ + 'session_category' => $sessionCategory, + ] +); +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); +$sessionList = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'all', $sessionCategory); +$totalItems = $plugin->getCatalogSubscriptionSessionList($first, $pageSize, $nameFilter, 'count', $sessionCategory); +$pagesCount = ceil($totalItems / $pageSize); +$pagination = BuyCoursesPlugin::returnPagination(api_get_self(), $currentPage, $pagesCount, $totalItems); + +// View +if (api_is_platform_admin()) { + $interbreadcrumb[] = [ + 'url' => 'subscriptions_sessions.php', + 'name' => $plugin->get_lang('AvailableCoursesConfiguration'), + ]; + $interbreadcrumb[] = [ + 'url' => 'paymentsetup.php', + 'name' => $plugin->get_lang('PaymentsConfiguration'), + ]; +} + +$templateName = $plugin->get_lang('CourseListOnSale'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$template = new Template($templateName); +$template->assign('search_filter_form', $form->returnForm()); +$template->assign('sessions_are_included', $includeSessions); +$template->assign('showing_sessions', true); +$template->assign('sessions', $sessionList); +$template->assign('pagination', $pagination); + +$courseList = $plugin->getCatalogSubscriptionCourseList($first, $pageSize, $nameFilter); +$coursesExist = true; +$sessionExist = true; +if (count($courseList) <= 0) { + $coursesExist = false; +} +$template->assign('coursesExist', $coursesExist); +$template->assign('sessionExist', $sessionExist); + +$content = $template->fetch('buycourses/view/subscription_catalog.tpl'); + +$template->assign('header', $templateName); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/buycourses/src/subscriptions_courses.php b/plugin/buycourses/src/subscriptions_courses.php new file mode 100644 index 000000000..1347e233e --- /dev/null +++ b/plugin/buycourses/src/subscriptions_courses.php @@ -0,0 +1,97 @@ +get('include_sessions') === 'true'; +$taxEnable = $plugin->get('tax_enable') === 'true'; + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$type = isset($_GET['type']) ? (int) $_GET['type'] : BuyCoursesPlugin::PRODUCT_TYPE_COURSE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +$qb = $plugin->getCourseList($first, $pageSize); +$query = $qb->getQuery(); +$courses = new Paginator($query, $fetchJoinCollection = true); +foreach ($courses as $course) { + $item = $plugin->getSubscriptionItemByProduct($course->getId(), BuyCoursesPlugin::PRODUCT_TYPE_COURSE); + $course->buyCourseData = []; + if ($item !== false) { + $course->buyCourseData = $item; + } +} + +$totalItems = count($courses); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => $type] +); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$templateName = $plugin->get_lang('AvailableCourses'); + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$tpl = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fa-calendar-alt'). + $plugin->get_lang('ConfigureSubscriptionsFrequencies'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/configure_frequency.php', + ['class' => 'btn btn-primary'] +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('courses', $courses); +$tpl->assign('course_pagination', $pagination); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('tax_enable', $taxEnable); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('buycourses/view/subscriptions.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/subscriptions_sessions.php b/plugin/buycourses/src/subscriptions_sessions.php new file mode 100644 index 000000000..a892d79e9 --- /dev/null +++ b/plugin/buycourses/src/subscriptions_sessions.php @@ -0,0 +1,101 @@ +get('include_sessions') === 'true'; + +if (!$includeSession) { + api_not_allowed(true); +} +$includeServices = $plugin->get('include_services') === 'true'; +$taxEnable = $plugin->get('tax_enable') === 'true'; + +api_protect_admin_script(true); + +Display::addFlash( + Display::return_message( + get_lang('Info').' - '.$plugin->get_lang('CoursesInSessionsDoesntDisplayHere'), + 'info' + ) +); + +$pageSize = BuyCoursesPlugin::PAGINATION_PAGE_SIZE; +$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1; +$first = $pageSize * ($currentPage - 1); + +// breadcrumbs +$interbreadcrumb[] = [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php', + 'name' => $plugin->get_lang('plugin_title'), +]; + +$htmlHeadXtra[] = api_get_css(api_get_path(WEB_PLUGIN_PATH).'buycourses/resources/css/style.css'); + +$templateName = $plugin->get_lang('AvailableCourses'); + +$tpl = new Template($templateName); + +$toolbar = Display::url( + Display::returnFontAwesomeIcon('fa-calendar-alt'). + $plugin->get_lang('ConfigureSubscriptionsFrequencies'), + api_get_path(WEB_PLUGIN_PATH).'buycourses/src/configure_frequency.php', + ['class' => 'btn btn-primary'] +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$toolbar]) +); + +$tpl->assign('product_type_course', BuyCoursesPlugin::PRODUCT_TYPE_COURSE); +$tpl->assign('product_type_session', BuyCoursesPlugin::PRODUCT_TYPE_SESSION); +$tpl->assign('sessions_are_included', $includeSession); +$tpl->assign('services_are_included', $includeServices); +$tpl->assign('tax_enable', $taxEnable); + +$query = CoursesAndSessionsCatalog::browseSessions(null, ['start' => $first, 'length' => $pageSize], true); +$sessions = new Paginator($query, $fetchJoinCollection = true); +foreach ($sessions as $session) { + $item = $plugin->getSubscriptionItemByProduct($session->getId(), BuyCoursesPlugin::PRODUCT_TYPE_SESSION); + $session->buyCourseData = []; + if ($item !== false) { + $session->buyCourseData = $item; + } +} + +$totalItems = count($sessions); +$pagesCount = ceil($totalItems / $pageSize); + +$pagination = BuyCoursesPlugin::returnPagination( + api_get_self(), + $currentPage, + $pagesCount, + $totalItems, + ['type' => BuyCoursesPlugin::PRODUCT_TYPE_SESSION] +); + +$tpl->assign('sessions', $sessions); +$tpl->assign('session_pagination', $pagination); + +if ($taxEnable) { + $globalParameters = $plugin->getGlobalParameters(); + $tpl->assign('global_tax_perc', $globalParameters['global_tax_perc']); + $tpl->assign('tax_applies_to', $globalParameters['tax_applies_to']); + $tpl->assign('tax_name', $globalParameters['tax_name']); +} + +$content = $tpl->fetch('buycourses/view/subscriptions.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/success.php b/plugin/buycourses/src/success.php new file mode 100644 index 000000000..2b2caffa4 --- /dev/null +++ b/plugin/buycourses/src/success.php @@ -0,0 +1,200 @@ +get('paypal_enable') === 'true'; + +if (!$paypalEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +$buyingCourse = false; +$buyingSession = false; + +switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + break; +} + +$paypalParams = $plugin->getPaypalParams(); +$pruebas = $paypalParams['sandbox'] == 1; +$paypalUsername = $paypalParams['username']; +$paypalPassword = $paypalParams['password']; +$paypalSignature = $paypalParams['signature']; + +require_once "paypalfunctions.php"; + +$form = new FormValidator( + 'success', + 'POST', + api_get_self(), + null, + null, + FormValidator::LAYOUT_INLINE +); +$form->addButton('confirm', $plugin->get_lang('ConfirmOrder'), 'check', 'success'); +$form->addButtonCancel($plugin->get_lang('CancelOrder'), 'cancel'); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + if (isset($formValues['cancel'])) { + $plugin->cancelSale($sale['id']); + unset($_SESSION['bc_sale_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/index.php'); + exit; + } + + $confirmPayments = ConfirmPayment($sale['price']); + + if ($confirmPayments['ACK'] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $confirmPayments['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + header('Location: ../index.php'); + exit; + } + + $transactionId = $confirmPayments['PAYMENTINFO_0_TRANSACTIONID']; + $transactionType = $confirmPayments['PAYMENTINFO_0_TRANSACTIONTYPE']; + + switch ($confirmPayments['PAYMENTINFO_0_PAYMENTSTATUS']) { + case 'Completed': + $saleIsCompleted = $plugin->completeSale($sale['id']); + if ($saleIsCompleted) { + Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) + ); + $plugin->storePayouts($sale['id']); + break; + } + + Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') + ); + break; + case 'Pending': + switch ($confirmPayments["PAYMENTINFO_0_PENDINGREASON"]) { + case 'address': + $purchaseStatus = $plugin->get_lang('PendingReasonByAddress'); + break; + case 'authorization': + $purchaseStatus = $plugin->get_lang('PendingReasonByAuthorization'); + break; + case 'echeck': + $purchaseStatus = $plugin->get_lang('PendingReasonByEcheck'); + break; + case 'intl': + $purchaseStatus = $plugin->get_lang('PendingReasonByIntl'); + break; + case 'multicurrency': + $purchaseStatus = $plugin->get_lang('PendingReasonByMulticurrency'); + break; + case 'order': + $purchaseStatus = $plugin->get_lang('PendingReasonByOrder'); + break; + case 'paymentreview': + $purchaseStatus = $plugin->get_lang('PendingReasonByPaymentReview'); + break; + case 'regulatoryreview': + $purchaseStatus = $plugin->get_lang('PendingReasonByRegulatoryReview'); + break; + case 'unilateral': + $purchaseStatus = $plugin->get_lang('PendingReasonByUnilateral'); + break; + case 'upgrade': + $purchaseStatus = $plugin->get_lang('PendingReasonByUpgrade'); + break; + case 'verify': + $purchaseStatus = $plugin->get_lang('PendingReasonByVerify'); + break; + case 'other': + default: + $purchaseStatus = $plugin->get_lang('PendingReasonByOther'); + break; + } + + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('PurchaseStatusX'), $purchaseStatus), + 'warning', + false + ) + ); + break; + default: + Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') + ); + break; + } + + unset($_SESSION['bc_sale_id']); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); + exit; +} + +$token = isset($_GET['token']) ? $_GET['token'] : null; + +if (empty($token)) { + api_not_allowed(true); +} + +$shippingDetails = GetShippingDetails($token); + +if ($shippingDetails['ACK'] !== 'Success') { + $erroMessage = vsprintf( + $plugin->get_lang('ErrorOccurred'), + [$expressCheckout['L_ERRORCODE0'], $shippingDetails['L_LONGMESSAGE0']] + ); + Display::addFlash( + Display::return_message($erroMessage, 'error', false) + ); + header('Location: ../index.php'); + exit; +} + +$interbreadcrumb[] = ["url" => "course_catalog.php", "name" => $plugin->get_lang('CourseListOnSale')]; + +$templateName = $plugin->get_lang('PaymentMethods'); +$tpl = new Template($templateName); + +if ($buyingCourse) { + $tpl->assign('course', $course); +} elseif ($buyingSession) { + $tpl->assign('session', $session); +} + +$tpl->assign('buying_course', $buyingCourse); +$tpl->assign('buying_session', $buyingSession); +$tpl->assign('title', $sale['product_name']); +$tpl->assign('price', $sale['price']); +$tpl->assign('currency', $sale['currency_id']); +$tpl->assign('user', api_get_user_info($sale['user_id'])); +$tpl->assign('form', $form->returnForm()); + +$content = $tpl->fetch('buycourses/view/success.tpl'); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/buycourses/src/tpv_error.php b/plugin/buycourses/src/tpv_error.php new file mode 100644 index 000000000..51680ceca --- /dev/null +++ b/plugin/buycourses/src/tpv_error.php @@ -0,0 +1,30 @@ +get('tpv_redsys_enable') === 'true'; + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + Display::return_message($plugin->get_lang('ErrorContactPlatformAdmin'), 'error') +); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/src/tpv_response.php b/plugin/buycourses/src/tpv_response.php new file mode 100644 index 000000000..1498390d9 --- /dev/null +++ b/plugin/buycourses/src/tpv_response.php @@ -0,0 +1,68 @@ +get('tpv_redsys_enable') === 'true'; + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$tpvRedsysParams = $plugin->getTpvRedsysParams(); + +$version = $_POST['Ds_SignatureVersion']; +$params = $_POST["Ds_MerchantParameters"]; +$signatureReceived = $_POST['Ds_Signature']; + +require_once '../resources/apiRedsys.php'; +$tpv = new RedsysAPI(); + +$decodec = $tpv->decodeMerchantParameters($params); +$kc = $tpvRedsysParams['kc']; +$signature = $tpv->createMerchantSignatureNotif($kc, $params); + +if ($signature === $signatureReceived) { + $saleId = (int) $tpv->getParameter("Ds_Order"); + $response = $tpv->getParameter("Ds_Response"); + + // other fields available + // $Ds_Amount=$miObj->getParameter("Ds_Amount"); + // $Ds_MerchantCode=$miObj->getParameter("Ds_MerchantCode"); + // $Ds_TransactionType=$miObj->getParameter("Ds_TransactionType"); + // $Ds_MerchantData=$miObj->getParameter("Ds_MerchantData"); + // $Ds_Date=$miObj->getParameter("Ds_Date"); + // $Ds_Hour=$miObj->getParameter("Ds_Hour"); + + $sale = $plugin->getSale($saleId); + if (empty($sale)) { + api_not_allowed(true); + } + + $buyingCourse = false; + $buyingSession = false; + + switch ($sale['product_type']) { + case BuyCoursesPlugin::PRODUCT_TYPE_COURSE: + $buyingCourse = true; + $course = $plugin->getCourseInfo($sale['product_id']); + break; + case BuyCoursesPlugin::PRODUCT_TYPE_SESSION: + $buyingSession = true; + $session = $plugin->getSessionInfo($sale['product_id']); + break; + } + + if ($response == "0000") { + $saleIsCompleted = $plugin->completeSale($sale['id']); + if ($saleIsCompleted) { + $plugin->storePayouts($sale['id']); + } + } +} diff --git a/plugin/buycourses/src/tpv_success.php b/plugin/buycourses/src/tpv_success.php new file mode 100644 index 000000000..cf9683ea2 --- /dev/null +++ b/plugin/buycourses/src/tpv_success.php @@ -0,0 +1,31 @@ +get('tpv_redsys_enable') === 'true'; + +if (!$tpvRedsysEnabled) { + api_not_allowed(true); +} + +$sale = $plugin->getSale($_SESSION['bc_sale_id']); + +if (empty($sale)) { + api_not_allowed(true); +} + +Display::addFlash( + $plugin->getSubscriptionSuccessMessage($sale) +); +//$plugin->storePayouts($sale['id']); + +unset($_SESSION['bc_sale_id']); +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'buycourses/src/course_catalog.php'); +exit; diff --git a/plugin/buycourses/uninstall.php b/plugin/buycourses/uninstall.php new file mode 100644 index 000000000..f1804e34d --- /dev/null +++ b/plugin/buycourses/uninstall.php @@ -0,0 +1,15 @@ +uninstall(); diff --git a/plugin/buycourses/update.php b/plugin/buycourses/update.php new file mode 100644 index 000000000..2b204456f --- /dev/null +++ b/plugin/buycourses/update.php @@ -0,0 +1,14 @@ +update(); diff --git a/plugin/buycourses/uploads/services/images/.gitignore b/plugin/buycourses/uploads/services/images/.gitignore new file mode 100644 index 000000000..cec9082b6 --- /dev/null +++ b/plugin/buycourses/uploads/services/images/.gitignore @@ -0,0 +1,3 @@ +* + +!.gitignore diff --git a/plugin/buycourses/view/catalog.tpl b/plugin/buycourses/view/catalog.tpl new file mode 100644 index 000000000..f6bbc8902 --- /dev/null +++ b/plugin/buycourses/view/catalog.tpl @@ -0,0 +1,223 @@ +
+ {% if sessions_are_included %} + + {% endif %} + +
+
+
+
+ {{ search_filter_form }} +
+
+
+ {% if showing_courses %} + {% for course in courses %} +
+
+
+
+ {{ course.title }} +
+
+
+ {% set course_description_url = _p.web_ajax ~ 'course_home.ajax.php?' ~ {'code': course.code, 'a': 'show_course_information'}|url_encode() %} +

+ {{ course.title }} +

+
    + {% for teacher in course.teachers %} +
  • {{ teacher }}
  • + {% endfor %} +
+

+ + {{ course.item.total_price_formatted }} + +

+ {% if course.enrolled == "YES" %} +
+ {{ 'TheUserIsAlreadyRegisteredInTheCourse'|get_plugin_lang('BuyCoursesPlugin') }} +
+ {% elseif course.enrolled == "NO" %} + + {% elseif course.enrolled == "TMP" %} +
{{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
+ {% endif %} +
+
+
+ {% endfor %} + {% endif %} + + {% if showing_sessions %} + {% for session in sessions %} +
+
+
+
+ {{ session.name }} +
+
+
+

+ {{ session.name }} +

+ {% if 'show_session_coach'|api_get_setting == 'true' %} +

{{ session.coach }}

+ {% endif %} +

+ + {% if session.duration %} + {{ 'SessionDurationXDaysTotal'|get_lang|format(session.duration) }} + {% else %} + {{ session.dates.display }} + {% endif %} +

+

+ + {{ session.item.total_price_formatted }} + +

+ + {% if session.enrolled == "YES" %} +
+ {{ 'TheUserIsAlreadyRegisteredInTheSession'|get_plugin_lang('BuyCoursesPlugin') }} +
+ {% elseif session.enrolled == "NO" %} + + {% elseif session.enrolled == "TMP" %} +
{{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
+ {% endif %} +
+
+
+ {% endfor %} + {% endif %} + + {% if showing_services %} + {% for service in services %} +
+
+ +
+

+ + {{ service.name }} + +

+
    +
  • + {{ 'Duration'|get_plugin_lang('BuyCoursesPlugin') }} + : {{ service.duration_days == 0 ? 'NoLimit'|get_lang : service.duration_days ~ ' ' ~ 'Days'|get_lang }} +
  • + {% if service.applies_to == 0 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'None'|get_lang }} +
  • + {% elseif service.applies_to == 1 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'User'|get_lang }} +
  • + {% elseif service.applies_to == 2 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Course'|get_lang }} +
  • + {% elseif service.applies_to == 3 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Session'|get_lang }} +
  • + {% elseif service.applies_to == 4 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'TemplateTitleCertificate'|get_plugin_lang('BuyCoursesPlugin') }} +
  • + {% endif %} +
  • {{ service.owner_name }}
  • +
+

+ + {{ service.total_price_formatted }} + +

+ +
+
+
+ {% endfor %} + {% endif %} +
+ {{ pagination }} +
+
+
+
+
diff --git a/plugin/buycourses/view/configure_frequency.tpl b/plugin/buycourses/view/configure_frequency.tpl new file mode 100644 index 000000000..7abce503f --- /dev/null +++ b/plugin/buycourses/view/configure_frequency.tpl @@ -0,0 +1,32 @@ +
+
+ {{ items_form }} +
+
+
+ + + + + + + + + + {% for frequency in frequencies_list %} + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'SubscriptionPeriodDuration'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
{{ frequency.name }}{{ frequency.duration }} +
+ + + +
+
+
\ No newline at end of file diff --git a/plugin/buycourses/view/configure_subscription.tpl b/plugin/buycourses/view/configure_subscription.tpl new file mode 100644 index 000000000..556f63e3d --- /dev/null +++ b/plugin/buycourses/view/configure_subscription.tpl @@ -0,0 +1,45 @@ +
+
+ {{ items_form }} +
+
+
+
+

{{ 'FrequencyConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ {{ frequency_form }} +
+
+
+ + + + + + + + + + {% for subscription in subscriptions %} + + + + + + {% endfor %} + +
{{ 'Duration'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Actions'|get_lang }}
{{ subscription.durationName }}{{ subscription.price }} {{ currencyIso }} + + + +
+
+
+
+
+
+ diff --git a/plugin/buycourses/view/coupons.tpl b/plugin/buycourses/view/coupons.tpl new file mode 100644 index 000000000..6280b9de7 --- /dev/null +++ b/plugin/buycourses/view/coupons.tpl @@ -0,0 +1,39 @@ +
+
+{{ form }} + +
+ + + + + + + + + + + + + + {% for coupon in coupon_list %} + + + + + + + + + + {% endfor %} + +
{{ 'CouponCode'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscountType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateStart'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateEnd'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDelivered'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
{{ coupon.code }}{{ coupon.discount_type }}{{ coupon.discount_value }}{{ coupon.valid_start | api_get_local_time }}{{ coupon.valid_end | api_get_local_time }}{{ coupon.delivered }} +
+ + + +
+
+
\ No newline at end of file diff --git a/plugin/buycourses/view/course_panel.tpl b/plugin/buycourses/view/course_panel.tpl new file mode 100644 index 000000000..e142ae6b6 --- /dev/null +++ b/plugin/buycourses/view/course_panel.tpl @@ -0,0 +1,48 @@ +
+ + + + + + + + + + + + + + + {% for sale in sale_list %} + + + + + + + + {% endfor %} + +
{{ 'Course'|get_lang }}{{ 'PaymentMethod'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
{{ sale.product_name }}{{ sale.payment_type }}{{ sale.currency ~ ' ' ~ sale.price }}{{ sale.date }}{{ sale.reference }}
+ + +
diff --git a/plugin/buycourses/view/export_report.tpl b/plugin/buycourses/view/export_report.tpl new file mode 100644 index 000000000..f9a859ae9 --- /dev/null +++ b/plugin/buycourses/view/export_report.tpl @@ -0,0 +1 @@ +{{ form }} diff --git a/plugin/buycourses/view/index.tpl b/plugin/buycourses/view/index.tpl new file mode 100644 index 000000000..969e9483d --- /dev/null +++ b/plugin/buycourses/view/index.tpl @@ -0,0 +1,117 @@ +{% if _u.is_admin %} +
+
+
+

{{ 'TitlePlugin'|get_plugin_lang('BuyCoursesPlugin') }}

+

{{ 'PluginPresentation'|get_plugin_lang('BuyCoursesPlugin') }}

+
    +
  • + {{ 'Instructions'|get_plugin_lang('BuyCoursesPlugin') }} +
      +
    • {{ 'InstructionsStepOne'|get_plugin_lang('BuyCoursesPlugin') }}
    • +
    • {{ 'InstructionsStepTwo'|get_plugin_lang('BuyCoursesPlugin') }}
    • +
    • {{ 'InstructionsStepThree'|get_plugin_lang('BuyCoursesPlugin') }}
    • +
    +
  • +
+
+
+
+{% endif %} + + diff --git a/plugin/buycourses/view/list.tpl b/plugin/buycourses/view/list.tpl new file mode 100644 index 000000000..b6c3fde7f --- /dev/null +++ b/plugin/buycourses/view/list.tpl @@ -0,0 +1,231 @@ +{% if sessions_are_included or services_are_included %} + +{% endif %} + +
+
+
+ + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + + + {% for item in courses %} + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + {% endfor %} + +
{{ 'Title'|get_lang }}{{ 'OfficialCode'|get_lang }}{{ 'VisibleInCatalog'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
+ {% if item.visibility == 0 %} + {{ 'CourseVisibilityClosed'|get_lang }} + {% elseif item.visibility == 1 %} + {{ 'Private'|get_lang }} + {% elseif item.visibility == 2 %} + {{ 'OpenToThePlatform'|get_lang }} + {% elseif item.visibility == 3 %} + {{ 'OpenToTheWorld'|get_lang }} + {% elseif item.visibility == 4 %} + {{ 'CourseVisibilityHidden'|get_lang }} + {% endif %} + + {{ item.title }} + + {{ item.code }} + + {{ item.code }} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.price_formatted }} + + {{ item.buyCourseData.tax_perc_show }} % + + + {{ 'Configure'|get_lang }} + +
+
+ {{ course_pagination }} +
+ + {% if sessions_are_included %} +
+
+ + + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + + + {% for item in sessions %} + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + {% endfor %} + +
{{ 'Title'|get_lang }}{{ 'StartDate'|get_lang }}{{ 'EndDate'|get_lang }}{{ 'VisibleInCatalog'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
+ {{ item.name }} + + {{ item.displayStartDate | api_convert_and_format_date(6)}} + + {{ item.displayEndDate |api_convert_and_format_date(6)}} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.price_formatted }} + + {{ item.buyCourseData.tax_perc_show }} % + + + + {{ 'Configure'|get_lang }} + +
+ {{ session_pagination }} +
+
+ {% endif %} + {% if services_are_included %} + + {{ 'NewService'| get_plugin_lang('BuyCoursesPlugin') }} + +
+
+
+
+ + + + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 4) %} + + {% endif %} + + + + + {% for item in services %} + + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 4) %} + + {% endif %} + + + {% endfor %} + +
{{ 'Service'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Description'|get_lang }}{{ 'Duration'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'VisibleInCatalog'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Owner'|get_lang }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
+ {{ item.name }} + + {{ item.description }} + + {% if item.duration_days == 0 %} + {{ 'NoLimit'|get_lang }} + {% else %} + {{ item.duration_days }} {{ 'Days'|get_lang }} + {% endif %} + + {% if item.visibility == 1 %} + + {% else %} + + {% endif %} + + {{ item.owner_name }} + + {{ item.price_formatted }} + + {% if item.tax_perc is null %} + {{ global_tax_perc }} % + {% else %} + {{ item.tax_perc }} % + {% endif %} + + + {{ 'Edit'|get_lang }} + +
+
+ {{ service_pagination }} +
+ {% endif %} +
diff --git a/plugin/buycourses/view/list_coupon.tpl b/plugin/buycourses/view/list_coupon.tpl new file mode 100644 index 000000000..447c56731 --- /dev/null +++ b/plugin/buycourses/view/list_coupon.tpl @@ -0,0 +1,49 @@ +
+
+{{ form }} + +
+ + + + + + + + + + + + + + {% for coupon in coupon_list %} + + + + + + + + + + {% endfor %} + +
{{ 'CouponCode'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscountType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateStart'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDateEnd'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDelivered'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
{{ coupon.code }}{{ coupon.discount_type }}{{ coupon.discount_value }}{{ coupon.valid_start | api_get_local_time }}{{ coupon.valid_end | api_get_local_time }}{{ coupon.delivered }} + {% if coupon.active == coupon_status_active %} +
+ + + +
+ {% endif %} + {% if coupon.active == coupon_status_disable %} +
+ + + +
+ {% endif %} +
+
\ No newline at end of file diff --git a/plugin/buycourses/view/list_subscription.tpl b/plugin/buycourses/view/list_subscription.tpl new file mode 100644 index 000000000..e69de29bb diff --git a/plugin/buycourses/view/message_confirm.tpl b/plugin/buycourses/view/message_confirm.tpl new file mode 100644 index 000000000..7f10c4a73 --- /dev/null +++ b/plugin/buycourses/view/message_confirm.tpl @@ -0,0 +1,14 @@ +
+
+
{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.date|api_convert_and_format_date(constant('DATE_TIME_FORMAT_LONG_24H')) }}
+
{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.reference }}
+
{{ 'UserName'|get_lang }}
+
{{ user.complete_name }}
+
{{ 'Product'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.product }}
+
{{ 'SalePrice'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.currency ~ ' ' ~ sale.price }}
+
+
diff --git a/plugin/buycourses/view/message_confirm_buyer.tpl b/plugin/buycourses/view/message_confirm_buyer.tpl new file mode 100644 index 000000000..ec386c39a --- /dev/null +++ b/plugin/buycourses/view/message_confirm_buyer.tpl @@ -0,0 +1,16 @@ +

{{ 'Dear'|get_lang }} {{ user.complete_name }},

+

{{ 'bc_subject'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.date|api_convert_and_format_date(constant('DATE_TIME_FORMAT_LONG_24H')) }}
+
{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.reference }}
+
{{ 'Product'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.product }}
+
{{ 'SalePrice'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.currency ~ ' ' ~ sale.price }}
+
+
+ +

{{ 'SignatureFormula'|get_lang }}

diff --git a/plugin/buycourses/view/message_transfer.tpl b/plugin/buycourses/view/message_transfer.tpl new file mode 100644 index 000000000..4d18ab1e1 --- /dev/null +++ b/plugin/buycourses/view/message_transfer.tpl @@ -0,0 +1,37 @@ +
+

{{ 'DearUser'|get_lang }}

+

{{ 'PurchaseDetailsIntro'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.date|api_convert_and_format_date(constant('DATE_TIME_FORMAT_LONG_24H')) }}
+
{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.reference }}
+
{{ 'UserName'|get_lang }}
+
{{ user.complete_name }}
+
{{ 'ProductName'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.product }}
+
{{ 'SalePrice'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ sale.currency ~ ' ' ~ sale.price }}
+
+

{{ 'BankAccountIntro'|get_plugin_lang('BuyCoursesPlugin')|format(sale.product) }}

+ + + + + + + + + + {% for account in transfer_accounts %} + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}
{{ account.name }}{{ account.account }}{{ account.swift }}
+

{{ info_email_extra }}

+

{{ 'PurchaseDetailsEnd'|get_plugin_lang('BuyCoursesPlugin') }}

+
diff --git a/plugin/buycourses/view/paymentsetup.tpl b/plugin/buycourses/view/paymentsetup.tpl new file mode 100644 index 000000000..cdb5920f9 --- /dev/null +++ b/plugin/buycourses/view/paymentsetup.tpl @@ -0,0 +1,171 @@ +
+
+

{{ 'GlobalConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ {{ global_config_form }} +
+
+
+
+ +

+ {{ 'PluginInstruction'|get_plugin_lang('BuyCoursesPlugin') }} + {{ 'ClickHere'|get_plugin_lang('BuyCoursesPlugin') }} +

+ +{% if paypal_enable == "true" %} +
+
+

{{ 'PayPalConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+

{{ 'InfoApiCredentials'|get_plugin_lang('BuyCoursesPlugin') }}

+
    +
  • {{ 'InfoApiStepOne'|get_plugin_lang('BuyCoursesPlugin') }}
  • +
  • {{ 'InfoApiStepTwo'|get_plugin_lang('BuyCoursesPlugin') }}
  • +
  • {{ 'InfoApiStepThree'|get_plugin_lang('BuyCoursesPlugin') }}
  • +
+
+
+ {{ paypal_form }} +
+
+
+
+{% endif %} + +{% if tpv_redsys_enable == "true" %} +
+
+

{{ 'TpvRedsysConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ {{ tpv_redsys_form }} +
+
+
+
+{% endif %} + +{% if commissions_enable == "true" %} +
+
+

{{ 'CommissionsConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+

{{ 'InfoCommissions'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+ {{ commission_form }} +
+
+
+
+{% endif %} + +{% if transfer_enable == "true" %} +
+
+

{{ 'TransfersConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ {{ transfer_form }} +
+
+
+ + + + + + + + + + + {% for account in transfer_accounts %} + + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Actions'|get_lang }}
{{ account.name }}{{ account.account }}{{ account.swift }} + + {{ 'Delete'|get_lang }} + +
+
+
+
+ {{ transfer_info_form }} +
+
+
+
+{% endif %} + +{% if culqi_enable == "true" %} +
+
+

{{ 'CulqiConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+

{{ 'InfoCulqiCredentials'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+ {{ culqi_form }} +
+
+
+
+{% endif %} + +{% if stripe_enable == "true" %} +
+
+

{{ 'StripeConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+

{{ 'InfoStripeCredentials'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+ {{ stripe_form }} +
+
+
+
+{% endif %} + +{% if cecabank_enable == "true" %} +
+
+

{{ 'CecabankConfig'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ {{ cecabank_form }} +
+
+
+
+{% endif %} diff --git a/plugin/buycourses/view/payout_panel.tpl b/plugin/buycourses/view/payout_panel.tpl new file mode 100644 index 000000000..01c5a4033 --- /dev/null +++ b/plugin/buycourses/view/payout_panel.tpl @@ -0,0 +1,50 @@ +
+ + + {% if not payout_list %} +

+ {{ 'WantToSellCourses'|get_plugin_lang('BuyCoursesPlugin') }} + {{ 'ClickHere'|get_plugin_lang('BuyCoursesPlugin') }} +

+ {% endif %} + + {% if payout_list %} + + + + + + + + + + + {% for payout in payout_list %} + + + + + + + {% endfor %} + +
{{ 'OrderReference'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'PayoutDate'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'Commission'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'PayPalAccount'| get_plugin_lang('BuyCoursesPlugin') }}
{{ payout.reference }}{{ payout.payout_date }}{{ payout.currency ~ ' ' ~ payout.commission }}{{ payout.paypal_account }}
+ {% endif %} + +
diff --git a/plugin/buycourses/view/payout_report.tpl b/plugin/buycourses/view/payout_report.tpl new file mode 100644 index 000000000..118169399 --- /dev/null +++ b/plugin/buycourses/view/payout_report.tpl @@ -0,0 +1,99 @@ +{{ form }} +
+ + + + + + + + + + + + {% for payout in payout_list %} + + + + + + {% if payout.paypal_account %} + + {% else %} + + {% endif %} + + {% endfor %} + +
{{ 'OrderReference'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'PayoutDate'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'Commission'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'Names'| get_lang }}{{ 'PayPalAccount'| get_plugin_lang('BuyCoursesPlugin') }}
{{ payout.reference }}{{ payout.payout_date }}{{ payout.currency ~ ' ' ~ payout.commission }}{{ payout.beneficiary }}{{ payout.paypal_account }}{{ 'NoPayPalAccountDetected'| get_plugin_lang('BuyCoursesPlugin') }}
+ + + +
+ +
+ +
+ + diff --git a/plugin/buycourses/view/paypal_payout.tpl b/plugin/buycourses/view/paypal_payout.tpl new file mode 100644 index 000000000..16fd803b2 --- /dev/null +++ b/plugin/buycourses/view/paypal_payout.tpl @@ -0,0 +1,131 @@ +
+ + + + + + + + + + + + + {% for payout in payout_list %} + + + + + + {% if payout.paypal_account %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
{{ 'OrderReference'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'Commission'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'PayPalAccount'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'| get_lang }}
{% if payout.paypal_account %} {% endif %}{{ payout.reference }}{{ payout.date }}{{ payout.currency ~ ' ' ~ payout.commission }}{{ payout.paypal_account }}{{ 'NoPayPalAccountDetected'| get_plugin_lang('BuyCoursesPlugin') }} + +
+ + +
+
+ +
+ + \ No newline at end of file diff --git a/plugin/buycourses/view/process.tpl b/plugin/buycourses/view/process.tpl new file mode 100644 index 000000000..862ce8d19 --- /dev/null +++ b/plugin/buycourses/view/process.tpl @@ -0,0 +1,167 @@ +
+ {% if item_type == 1 %} + {% set back_url = _p.web_plugin ~ 'buycourses/src/course_catalog.php' %} + {% elseif item_type == 2 %} + {% set back_url = _p.web_plugin ~ 'buycourses/src/session_catalog.php' %} + {% else %} + {% set back_url = _p.web_plugin ~ 'buycourses/src/service_catalog.php' %} + {% endif %} + + + {{ + +
+ +
+
+
+
+
+ {% if buying_course %} +
+
+ + {{ course.title }} + + {% if course.tax_enable %} +
+ {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ course.item.price_formatted }} +
+ {{ course.tax_name }} ({{ course.item.tax_perc_show }}%): + {{ course.item.tax_amount_formatted }} +
+ {% endif %} +
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ course.item.total_price_formatted }} +
+ {% if course.item.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ course.item.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+
+
+
+

+ + {{ course.title }} + +

+ {% if course.description %} +
+ {{ course.description }} +
+ {% endif %} + + {% if course.teachers %} +
+

+ {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + {% for teacher in course.teachers %} + + {{ teacher.name }}, + {% endfor %} +

+
+ {% endif %} +
+
+
+ {% elseif buying_session %} +
+
+ {{ session.name }} + {% if session.tax_enable %} +
+ {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ session.item.price_formatted }} +
+ {{ session.tax_name }} ({{ session.item.tax_perc_show }}%): + {{ session.item.tax_amount_formatted }} +
+ {% endif %} +
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ session.item.total_price_formatted }} +
+ {% if session.item.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ session.item.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+
+
+
+

{{ session.name }}

+ {% if session.description %} +
+ {{ session.description }} +
+ {% endif %} +
+ {{ session.dates.display }} +
+
+
+ {% for course in session.courses %} +

+ {{ course.title }} +

+ {% if course.coaches|length %} +

+ {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + + {% for coach in course.coaches %} + + {{ coach.name }}, + {% endfor %} +

+ {% endif %} + {% endfor %} +
+
+
+
+ {% endif %} +
+
+

{{ 'PaymentMethods'|get_plugin_lang('BuyCoursesPlugin') }}

+ {{ form }} +
+
+
+
+
+ diff --git a/plugin/buycourses/view/process_confirm.tpl b/plugin/buycourses/view/process_confirm.tpl new file mode 100644 index 000000000..d37208c17 --- /dev/null +++ b/plugin/buycourses/view/process_confirm.tpl @@ -0,0 +1,283 @@ +
+
+
+
+
+

{{ 'PurchaseData'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+ {% if buying_course %} +
+
+ + {{ course.title }} + +
+
+

+ {{ course.title }} +

+
    + {% for teacher in course.teachers %} +
  • {{ teacher.name }}
  • + {% endfor %} +
+

+ + {{ course.item.total_price_formatted }} + +

+

+
+
+ {% elseif buying_session %} +
+
+

+ {{ session.name }} +

+
+
+

{{ session.name }}

+

{{ session.dates.display }}

+
    + {% for course in session.courses %} +
  • + {{ course.title }} + {% if course.coaches|length %} +
      + {% for coach in course.coaches %} +
    • {{ coach.name }}
    • + {% endfor %} +
    + {% endif %} +
  • + {% endfor %} +
+

+ + {{ session.item.total_price_formatted }} + +

+

+
+
+ {% elseif buying_service %} +
+
+ + {{ service.name }} + +
+
+
+
+

+ {{ service.name }} +

+
    + {% if service.applies_to == 0 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'None'|get_lang }} +
  • + {% elseif service.applies_to == 1 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'User'|get_lang }} +
  • + {% elseif service.applies_to == 2 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Course'|get_lang }} +
  • + {% elseif service.applies_to == 3 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Session'|get_lang }} +
  • + {% elseif service.applies_to == 4 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'TemplateTitleCertificate'|get_lang }} +
  • + {% endif %} +
  • + + {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} + : {{ service_item.total_price_formatted }} + / {{ service.duration_days == 0 ? 'NoLimit'|get_lang : service.duration_days ~ ' ' ~ 'Days'|get_lang }} +
  • +
  • {{ service.owner.name }}
  • + {% if service.description %} +
  • {{ service.description }}
  • + {% endif %} +
+

+ + {{ service_item.total_price_formatted }} + +

+

+
+
+ {% endif %} +
+
+
+ {% if terms %} +
+
+
+

{{ 'TermsAndConditions'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ +
+
+ +
+
+
+
+
+ {% endif %} +
+ +{% if is_bank_transfer %} +
+
+ +
+ + + + + + + + + + {% for account in transfer_accounts %} + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}
{{ account.name }}{{ account.account }}{{ account.swift }}
+
+

{{ 'OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+{% endif %} + +
+
+ {{ form }} +
+
+ diff --git a/plugin/buycourses/view/sales_report.tpl b/plugin/buycourses/view/sales_report.tpl new file mode 100644 index 000000000..c7c7f4199 --- /dev/null +++ b/plugin/buycourses/view/sales_report.tpl @@ -0,0 +1,55 @@ + +
+
+{{ form }} + +
+ +
+
+
+ + diff --git a/plugin/buycourses/view/service_information.tpl b/plugin/buycourses/view/service_information.tpl new file mode 100644 index 000000000..24a8ad6e6 --- /dev/null +++ b/plugin/buycourses/view/service_information.tpl @@ -0,0 +1,108 @@ +
+ +
+
+
+
+
+
+ {% if service.video_url %} +
+
+ {{ essence.replace(service.video_url) }} +
+
+ {% elseif service.image %} +
+ + {{ service.name }} + +
+ {% endif %} +
+ +
+
+ {% if service.description %} +
+ {{ service.description }} +
+ {% endif %} +
+ {% if service.applies_to != 0 %} +

{{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} : + {% if service.applies_to == 1 %} + {{ 'User'|get_lang }} + {% elseif service.applies_to == 2 %} + {{ 'Course'|get_lang }} + {% elseif service.applies_to == 3 %} + {{ 'Session'|get_lang }} + {% elseif service.applies_to == 4 %} + {{ 'TemplateTitleCertificate'|get_lang }} + {% endif %} +

+ {% endif %} +
+
+
+
+
+ {{ 'Total'|get_lang }} + {{ service.total_price_formatted }} +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+ {{ service.service_information }} +
+
+
+
+
+
diff --git a/plugin/buycourses/view/service_message_transfer.tpl b/plugin/buycourses/view/service_message_transfer.tpl new file mode 100644 index 000000000..d15f9b224 --- /dev/null +++ b/plugin/buycourses/view/service_message_transfer.tpl @@ -0,0 +1,36 @@ +
+

{{ 'DearUser'|get_lang }}

+

{{ 'PurchaseDetailsIntro'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ service_sale.buy_date|api_convert_and_format_date(constant('DATE_TIME_FORMAT_LONG_24H')) }}
+
{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ service_sale.reference }}
+
{{ 'UserName'|get_lang }}
+
{{ service_sale.buyer }}
+
{{ 'Service'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ service_sale.name }}
+
{{ 'SalePrice'|get_plugin_lang('BuyCoursesPlugin') }}
+
{{ service_sale.currency ~ ' ' ~ service_sale.price }}
+
+

{{ 'BankAccountIntro'|get_plugin_lang('BuyCoursesPlugin')|format(service_sale.name) }}

+ + + + + + + + + + {% for account in transfer_accounts %} + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}
{{ account.name }}{{ account.account }}{{ account.swift }}
+

{{ 'PurchaseDetailsEnd'|get_plugin_lang('BuyCoursesPlugin') }}

+
diff --git a/plugin/buycourses/view/service_panel.tpl b/plugin/buycourses/view/service_panel.tpl new file mode 100644 index 000000000..08c400622 --- /dev/null +++ b/plugin/buycourses/view/service_panel.tpl @@ -0,0 +1,85 @@ +
+ + + + + + + + + + + + + + {% for sale in sale_list %} + + + + + + + + + + {% endfor %} + +
{{ 'Service'| get_plugin_lang('BuyCoursesPlugin') }}{{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'ServiceSaleInfo'|get_plugin_lang('BuyCoursesPlugin') }}
{{ sale.name }}{{ sale.service_type }}{{ sale.currency ~ ' ' ~ sale.price }}{{ sale.date | api_get_local_time }}{{ sale.reference }} + {{ 'Info'|get_lang }} +
+
+ diff --git a/plugin/buycourses/view/service_process.tpl b/plugin/buycourses/view/service_process.tpl new file mode 100644 index 000000000..037a76a66 --- /dev/null +++ b/plugin/buycourses/view/service_process.tpl @@ -0,0 +1,120 @@ + + +
+
+
+
+
+ {% if buying_service %} +
+
+ + {{ service.name }} + + {% if service.tax_enable %} +
+ {{ 'Price'|get_plugin_lang('BuyCoursesPlugin')}} : + {{ service.price_formatted }} +
+ {{ service.tax_name }} ({{ service.tax_perc_show }}%): + {{ service.tax_amount_formatted }} +
+
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin')}} : + {{ service.total_price_formatted }} +
+ {% if service.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ service.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+ {% else %} +
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin')}} : + {{ service.total_price_formatted }} +
+ {% if service.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ service.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+ {% endif %} + +
+
+
+

+ {{ service.name }} +

+ {% if service.description %} +
+ {{ service.description }} +
+ {% endif %} +
    + {% if service.applies_to %} +
  • + + {% if service.applies_to == 0 %} + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') ~ ' ' ~ 'None'|get_lang }} + {% elseif service.applies_to == 1 %} + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') ~ ' ' ~ 'User'|get_lang }} + {% elseif service.applies_to == 2 %} + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') ~ ' ' ~ 'Course'|get_lang }} + {% elseif service.applies_to == 3 %} + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') ~ ' ' ~ 'Session'|get_lang }} + {% elseif service.applies_to == 4 %} + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') ~ ' ' ~ 'TemplateTitleCertificate'|get_lang }} + {% endif %} +
  • + {% endif %} +
  • + + {{ service.duration_days == 0 ? 'NoLimit'|get_lang : service.duration_days ~ ' ' ~ 'Days'|get_lang }} +
  • +
  • {{ service.owner_name }}
  • + +
+
+
+
+ {% endif %} +
+
+

{{ 'PaymentMethods'|get_plugin_lang('BuyCoursesPlugin') }}

+ {{ form }} +
+
+
+
+
+ diff --git a/plugin/buycourses/view/service_sales_report.tpl b/plugin/buycourses/view/service_sales_report.tpl new file mode 100644 index 000000000..39ae19e59 --- /dev/null +++ b/plugin/buycourses/view/service_sales_report.tpl @@ -0,0 +1,121 @@ + +
+
+
+
+

{{ 'Filter'|get_lang }}

+ {{ form }} +
+
+
+ + + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + + + {% for sale in sale_list %} + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + {% endfor %} + +
{{ 'ServiceName'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderStatus'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Coupon'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Invoice'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'ServiceSaleInfo'|get_plugin_lang('BuyCoursesPlugin') }}
{{ sale.name }}{{ sale.reference }} + {% if sale.status == sale_status_cancelled %} + {{ 'SaleStatusCancelled'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_pending %} + {{ 'SaleStatusPending'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_completed %} + {{ 'SaleStatusCompleted'|get_plugin_lang('BuyCoursesPlugin') }} + {% endif %} + {{ sale.buy_date | api_get_local_time}}{{ sale.service.total_price }}{{ sale.total_discount }}{{ sale.coupon_code }} + {% if sale.invoice == 1 %} + + {{ 'InvoiceView'|get_plugin_lang('BuyCoursesPlugin') }} +
{{ sale.num_invoice }} +
+ {% endif %} +
+ + {{ 'Info'|get_lang }} + +
+
+
+
+ + diff --git a/plugin/buycourses/view/session_panel.tpl b/plugin/buycourses/view/session_panel.tpl new file mode 100644 index 000000000..265b8d3e4 --- /dev/null +++ b/plugin/buycourses/view/session_panel.tpl @@ -0,0 +1,48 @@ +
+ + + + + + + + + + + + + + + {% for sale in sale_list %} + + + + + + + + {% endfor %} + +
{{ 'Session'|get_lang }}{{ 'PaymentMethod'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}
{{ sale.product_name }}{{ sale.payment_type }}{{ sale.currency ~ ' ' ~ sale.price }}{{ sale.date }}{{ sale.reference }}
+ + +
diff --git a/plugin/buycourses/view/subscription_add.tpl b/plugin/buycourses/view/subscription_add.tpl new file mode 100644 index 000000000..ce8a838a9 --- /dev/null +++ b/plugin/buycourses/view/subscription_add.tpl @@ -0,0 +1,37 @@ +
+
+ {{ items_form }} +
+
+ + diff --git a/plugin/buycourses/view/subscription_catalog.tpl b/plugin/buycourses/view/subscription_catalog.tpl new file mode 100644 index 000000000..7edbc2ccd --- /dev/null +++ b/plugin/buycourses/view/subscription_catalog.tpl @@ -0,0 +1,122 @@ +
+ {% if sessions_are_included %} + + {% endif %} + +
+
+
+
+ {{ search_filter_form }} +
+
+
+ {% if showing_courses %} + {% for course in courses %} +
+
+
+
+ {{ course.title }} +
+
+
+ {% set course_description_url = _p.web_ajax ~ 'course_home.ajax.php?' ~ {'code': course.code, 'a': 'show_course_information'}|url_encode() %} +

+ {{ course.title }} +

+
    + {% for teacher in course.teachers %} +
  • {{ teacher }}
  • + {% endfor %} +
+ {% if course.enrolled == "YES" %} +
+ {{ 'TheUserIsAlreadyRegisteredInTheCourse'|get_plugin_lang('BuyCoursesPlugin') }} +
+ {% elseif course.enrolled == "NO" %} + + {% elseif course.enrolled == "TMP" %} +
{{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
+ {% endif %} +
+
+
+ {% endfor %} + {% endif %} + + {% if showing_sessions %} + {% for session in sessions %} +
+
+
+
+ {{ session.name }} +
+
+
+

+ {{ session.name }} +

+ {% if 'show_session_coach'|api_get_setting == 'true' %} +

{{ session.coach }}

+ {% endif %} +

+ + {% if session.duration %} + {{ 'SessionDurationXDaysTotal'|get_lang|format(session.duration) }} + {% else %} + {{ session.dates.display }} + {% endif %} +

+ {% if session.enrolled == "YES" %} +
+ {{ 'TheUserIsAlreadyRegisteredInTheSession'|get_plugin_lang('BuyCoursesPlugin') }} +
+ {% elseif session.enrolled == "NO" %} + + {% elseif session.enrolled == "TMP" %} +
{{ 'WaitingToReceiveThePayment'|get_plugin_lang('BuyCoursesPlugin') }}
+ {% endif %} +
+
+
+ {% endfor %} + {% endif %} +
+ {{ pagination }} +
+
+
+
+
diff --git a/plugin/buycourses/view/subscription_process.tpl b/plugin/buycourses/view/subscription_process.tpl new file mode 100644 index 000000000..5df69fdd9 --- /dev/null +++ b/plugin/buycourses/view/subscription_process.tpl @@ -0,0 +1,187 @@ +
+ {% if item_type == 1 %} + {% set back_url = _p.web_plugin ~ 'buycourses/src/subscription_course_catalog.php' %} + {% elseif item_type == 2 %} + {% set back_url = _p.web_plugin ~ 'buycourses/src/subscription_session_catalog.php' %} + {% endif %} + + + {{ + +
+ +
+
+
+
+
+ {% if buying_course %} +
+
+ + {{ course.title }} + + {% if course.tax_enable %} +
+ {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.price_formatted }} +
+ {{ course.tax_name }} ({{ subscription.item.tax_perc_show }}%): + {{ subscription.tax_amount_formatted }} +
+ {% endif %} +
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.total_price_formatted }} +
+ {% if course.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ course.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'SelecSubscription'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_subscription }} +
+
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+
+
+
+

+ + {{ course.title }} + +

+ {% if course.description %} +
+ {{ course.description }} +
+ {% endif %} + + {% if course.teachers %} +
+

+ {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + {% for teacher in course.teachers %} + + {{ teacher.name }}, + {% endfor %} +

+
+ {% endif %} +
+
+
+ {% elseif buying_session %} +
+
+ {{ session.name }} + {% if session.tax_enable %} +
+ {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.price_formatted }} +
+ {{ session.tax_name }} ({{ subscription.item.tax_perc_show }}%): + {{ subscription.tax_amount_formatted }} +
+ {% endif %} +
+ {{ 'Total'|get_plugin_lang('BuyCoursesPlugin') }} : + {{ subscription.total_price_formatted }} +
+ {% if session.has_coupon %} +
+ {{ 'DiscountAmount'|get_plugin_lang('BuyCoursesPlugin') }}: + {{ session.discount_amount_formatted }} +
+ {% endif %} +
+ {{ 'SelecSubscription'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_subscription }} +
+
+ {{ 'DoYouHaveACoupon'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+ {{ form_coupon }} +
+
+
+
+

{{ session.name }}

+ {% if session.description %} +
+ {{ session.description }} +
+ {% endif %} +
+ {{ session.dates.display }} +
+
+
+ {% for course in session.courses %} +

+ {{ course.title }} +

+ {% if course.coaches|length %} +

+ {{ 'Teachers'|get_plugin_lang('BuyCoursesPlugin') }} : + + {% for coach in course.coaches %} + + {{ coach.name }}, + {% endfor %} +

+ {% endif %} + {% endfor %} +
+
+
+
+ {% endif %} +
+
+

{{ 'PaymentMethods'|get_plugin_lang('BuyCoursesPlugin') }}

+ {% if message_payment %} + {{ message_payment }} + {% else %} + {{ form }} + {% endif %} +
+
+
+
+
+ diff --git a/plugin/buycourses/view/subscription_process_confirm.tpl b/plugin/buycourses/view/subscription_process_confirm.tpl new file mode 100644 index 000000000..daaad8808 --- /dev/null +++ b/plugin/buycourses/view/subscription_process_confirm.tpl @@ -0,0 +1,283 @@ +
+
+
+
+
+

{{ 'PurchaseData'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+ {% if buying_course %} +
+
+ + {{ course.title }} + +
+
+

+ {{ course.title }} +

+
    + {% for teacher in course.teachers %} +
  • {{ teacher.name }}
  • + {% endfor %} +
+

+ + {{ course.item.total_price_formatted }} + +

+

+
+
+ {% elseif buying_session %} +
+
+

+ {{ session.name }} +

+
+
+

{{ session.name }}

+

{{ session.dates.display }}

+
    + {% for course in session.courses %} +
  • + {{ course.title }} + {% if course.coaches|length %} +
      + {% for coach in course.coaches %} +
    • {{ coach }}
    • + {% endfor %} +
    + {% endif %} +
  • + {% endfor %} +
+

+ + {{ session.item.total_price_formatted }} + +

+

+
+
+ {% elseif buying_service %} +
+
+ + {{ service.name }} + +
+
+
+
+

+ {{ service.name }} +

+
    + {% if service.applies_to == 0 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'None'|get_lang }} +
  • + {% elseif service.applies_to == 1 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'User'|get_lang }} +
  • + {% elseif service.applies_to == 2 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Course'|get_lang }} +
  • + {% elseif service.applies_to == 3 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'Session'|get_lang }} +
  • + {% elseif service.applies_to == 4 %} +
  • + {{ 'AppliesTo'|get_plugin_lang('BuyCoursesPlugin') }} {{ 'TemplateTitleCertificate'|get_lang }} +
  • + {% endif %} +
  • + + {{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }} + : {{ service_item.total_price_formatted }} + / {{ service.duration_days == 0 ? 'NoLimit'|get_lang : service.duration_days ~ ' ' ~ 'Days'|get_lang }} +
  • +
  • {{ service.owner.name }}
  • + {% if service.description %} +
  • {{ service.description }}
  • + {% endif %} +
+

+ + {{ service_item.total_price_formatted }} + +

+

+
+
+ {% endif %} +
+
+
+ {% if terms %} +
+
+
+

{{ 'TermsAndConditions'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+
+
+ +
+
+ +
+
+
+
+
+ {% endif %} +
+ +{% if is_bank_transfer %} +
+
+ +
+ + + + + + + + + + {% for account in transfer_accounts %} + + + + + + {% endfor %} + +
{{ 'Name'|get_lang }}{{ 'BankAccount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'SWIFT'|get_plugin_lang('BuyCoursesPlugin') }}
{{ account.name }}{{ account.account }}{{ account.swift }}
+
+

{{ 'OnceItIsConfirmedYouWillReceiveAnEmailWithTheBankInformationAndAnOrderReference'|get_plugin_lang('BuyCoursesPlugin') }}

+
+
+{% endif %} + +
+
+ {{ form }} +
+
+ diff --git a/plugin/buycourses/view/subscription_sales_report.tpl b/plugin/buycourses/view/subscription_sales_report.tpl new file mode 100644 index 000000000..c88207224 --- /dev/null +++ b/plugin/buycourses/view/subscription_sales_report.tpl @@ -0,0 +1,122 @@ + +
+
+{{ form }} + +
+ + + + + + + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + + + {% for sale in sale_list %} + + + + + + + + + + + + + {% if invoicing_enable %} + + {% endif %} + + + {% endfor %} + +
{{ 'OrderReference'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderStatus'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'OrderDate'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'PaymentMethod'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Price'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'CouponDiscount'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Coupon'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'ProductType'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Name'|get_lang }}{{ 'UserName'|get_lang }}{{ 'Email'|get_lang }}{{ 'Invoice'|get_plugin_lang('BuyCoursesPlugin') }}{{ 'Options'|get_lang }}
{{ sale.reference }} + {% if sale.status == sale_status_canceled %} + {{ 'SaleStatusCanceled'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_pending %} + {{ 'SaleStatusPending'|get_plugin_lang('BuyCoursesPlugin') }} + {% elseif sale.status == sale_status_completed %} + {{ 'SaleStatusCompleted'|get_plugin_lang('BuyCoursesPlugin') }} + {% endif %} + {{ sale.date | api_get_local_time }}{{ sale.payment_type }}{{ sale.total_price }}{{ sale.total_discount }}{{ sale.coupon_code }}{{ sale.product_type }}{{ sale.product_name }}{{ sale.complete_user_name }}{{ sale.email }} + {% if sale.invoice == 1 %} + + {{ 'InvoiceView'|get_plugin_lang('BuyCoursesPlugin') }} +
{{ sale.num_invoice }} +
+ {% endif %} +
+ {% if sale.status == sale_status_pending %} + + {% endif %} +
+
+ + diff --git a/plugin/buycourses/view/subscriptions.tpl b/plugin/buycourses/view/subscriptions.tpl new file mode 100644 index 000000000..883ba0f2a --- /dev/null +++ b/plugin/buycourses/view/subscriptions.tpl @@ -0,0 +1,152 @@ +{% if sessions_are_included %} + +{% endif %} + +
+
+
+ + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + + + {% for item in courses %} + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 2) %} + + {% endif %} + + + {% endfor %} + +
{{ 'Title'|get_lang }}{{ 'OfficialCode'|get_lang }}{{ 'HasSubscriptions'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
+ {% if item.visibility == 0 %} + {{ 'CourseVisibilityClosed'|get_lang }} + {% elseif item.visibility == 1 %} + {{ 'Private'|get_lang }} + {% elseif item.visibility == 2 %} + {{ 'OpenToThePlatform'|get_lang }} + {% elseif item.visibility == 3 %} + {{ 'OpenToTheWorld'|get_lang }} + {% elseif item.visibility == 4 %} + {{ 'CourseVisibilityHidden'|get_lang }} + {% endif %} + + {{ item.title }} + + {{ item.code }} + + {{ item.code }} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.tax_perc_show }} % + + {% if item.buyCourseData %} + + {{ 'Configure'|get_lang }} + + {% else %} + + {{ 'Configure'|get_lang }} + + {% endif %} +
+
+ {{ course_pagination }} +
+ + {% if sessions_are_included %} +
+
+ + + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + + + {% for item in sessions %} + + + + + + {% if tax_enable and (tax_applies_to == 1 or tax_applies_to == 3) %} + + {% endif %} + + + {% endfor %} + +
{{ 'Title'|get_lang }}{{ 'StartDate'|get_lang }}{{ 'EndDate'|get_lang }}{{ 'HasSubscriptions'|get_plugin_lang('BuyCoursesPlugin') }}{{ tax_name }}{{ 'Options'|get_lang }}
+ {{ item.name }} + + {{ item.displayStartDate | api_convert_and_format_date(6)}} + + {{ item.displayEndDate |api_convert_and_format_date(6)}} + + {% if item.buyCourseData %} + + {% else %} + + {% endif %} + + {{ item.buyCourseData.tax_perc_show }} % + + {% if item.buyCourseData %} + + {{ 'Configure'|get_lang }} + + {% else %} + + {{ 'Configure'|get_lang }} + + {% endif %} +
+ {{ session_pagination }} +
+
+ {% endif %} +
diff --git a/plugin/buycourses/view/success.tpl b/plugin/buycourses/view/success.tpl new file mode 100644 index 000000000..cc25e8ece --- /dev/null +++ b/plugin/buycourses/view/success.tpl @@ -0,0 +1,70 @@ +
+ {{ 'PayPalPaymentOKPleaseConfirm'|get_plugin_lang('BuyCoursesPlugin') }} +
+
+
+ +
+
{{ 'Name'|get_lang }} +
+
{{ user.complete_name }}
+
{{ 'Username'|get_lang }} +
+
{{ user.username }}
+
{{ 'EmailAddress'|get_lang }} +
+
{{ user.email }}
+
+
+
+ {% if buying_course %} +
+
+

+ {{ course.title }} +

+

{{ course.currency }} {{ course.price }}

+
+
+ +
    + {% for teacher in course.teachers %} +
  • {{ teacher.name }}
  • + {% endfor %} +
+

+ + {{ 'Description'|get_lang }} + +

+
+
+ {% elseif buying_session %} + +
+
+

+ {{ session.name }} +

+

{{ session.currency }} {{ session.price }}

+
+
+

{{ session.dates.display }}

+
+ {% for course in session.courses %} +
{{ course.title }}
+ {% for coach in course.coaches %} +
{{ coach.name }}
+ {% endfor %} + {% endfor %} +
+
+
+ {% endif %} +
+
+ +{{ form }} diff --git a/plugin/card_game/LICENSE.txt b/plugin/card_game/LICENSE.txt new file mode 100644 index 000000000..9e419e042 --- /dev/null +++ b/plugin/card_game/LICENSE.txt @@ -0,0 +1,674 @@ +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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/plugin/card_game/README.md b/plugin/card_game/README.md new file mode 100644 index 000000000..f7fd5ce33 --- /dev/null +++ b/plugin/card_game/README.md @@ -0,0 +1,26 @@ +Card reveal game Chamilo plugin +=============================== +This plugin adds a little game to the interface to encourage users to connect +every day. + +# Installation + +To install, enable the plugin, then go to "Regions" in the administration panel +and add the region "pre_footer" to the plugin. Save. +The plugin should appear as a little icon in the lower-right side of your user +picture in the left column of the "My courses" list. + +## Migrating from a non-official version +This plugin was initially designed by _Les Compagnons Bâtisseurs_. If you had +used it before its review and integration into Chamilo, you will need to +execute the following changes in your database to update its structure. + +##### Database changes +You need execute these SQL queries in your database if you are upgrading to +Chamilo 1.11.8 and the card_game plugin was already installed in your previous version. + +```sql +ALTER TABLE plugin_card_game CHANGE COLUMN idUser user_id INT NOT NULL; +ALTER TABLE plugin_card_game CHANGE COLUMN dateAcces access_date date default NULL; + +``` \ No newline at end of file diff --git a/plugin/card_game/card_game.php b/plugin/card_game/card_game.php new file mode 100644 index 000000000..d685a9519 --- /dev/null +++ b/plugin/card_game/card_game.php @@ -0,0 +1,51 @@ +'; + $fh .= ''; + + $fh .= ''; + $fh .= ''; + $fh .= ''; + + $fh .= ''; + + $userId = api_get_user_id(); + + // Look if the user can still try playing today + $cardGameSession = Session::read('cardgame'); + if (!empty($cardGameSession)) { + // If we've already loaded the cardgame in this session, then there's + // a chance we've already played + if (isset($userId)) { + $sqlCount = "SELECT access_date FROM plugin_card_game WHERE user_id = $userId"; + $resultCount = Database::query($sqlCount)->rowCount(); + + if ($resultCount === 0) { + // If there is no database entry for this user, insert one + // without the 'parts' field (because he has not played yet) + // @todo change date call + $sql = "INSERT INTO plugin_card_game (user_id, access_date, pan) + VALUES ($userId, DATE_ADD(CURDATE(), INTERVAL -1 DAY), 1);"; + $resultInsert = Database::query($sql); + Session::write('cardgame', 'havedeck'); + } else { + // If there is already one or more records in the database, + // get the number of records for today + // @todo change date call + $sqlDate = "SELECT access_date + FROM plugin_card_game + WHERE access_date = CURDATE() + AND user_id = $userId"; + $resultDate = Database::query($sqlDate)->rowCount(); + + if ($resultDate == 0) { + // If there are records, but none for today, set the + // 'cardgame' session variable and add the + // #havedeckcardgame element to the page (it will get + // picked up by JS later on) + Session::write('cardgame', 'havedeck'); + $fh .= '
'; + } else { + // If the user already played today, set the session + // 'cardgame' variable to 'done' and do not add + // an #havedeckcardgame element + Session::write('cardgame', 'done'); + } + } + } + } else { + Session::write('cardgame', 'havedeck'); + $fh .= '
'; + } + + $parts = '1'; + $pan = '1'; + + if (isset($userId)) { + try { + $sqlParts = "SELECT parts, pan FROM plugin_card_game WHERE user_id = $userId"; + $resultParts = Database::query($sqlParts); + while ($part = Database::fetch_array($resultParts)) { + $parts = $part['parts']; + $pan = $part['pan']; + } + } catch (Exception $e) { + echo 'Exception: ', $e->getMessage(), "\n"; + } + } + + echo ''; + echo ''; + + echo $fh; +} diff --git a/plugin/card_game/install.php b/plugin/card_game/install.php new file mode 100644 index 000000000..5683e4b96 --- /dev/null +++ b/plugin/card_game/install.php @@ -0,0 +1,9 @@ +install(); diff --git a/plugin/card_game/lang/english.php b/plugin/card_game/lang/english.php new file mode 100644 index 000000000..0130a7117 --- /dev/null +++ b/plugin/card_game/lang/english.php @@ -0,0 +1,5 @@ +Plugins). + * + * @package chamilo.plugin card_game + * + * @author Damien Renou + */ +/* Plugin config */ + +// The plugin title. +$plugin_info['title'] = 'Card game'; +// The comments that go with the plugin. +$plugin_info['comment'] = "Show a card game"; +// The plugin version. +$plugin_info['version'] = '1.2'; +// The plugin author. +$plugin_info['author'] = 'Damien Renou'; diff --git a/plugin/card_game/resources/ajax.card.php b/plugin/card_game/resources/ajax.card.php new file mode 100644 index 000000000..7441207a8 --- /dev/null +++ b/plugin/card_game/resources/ajax.card.php @@ -0,0 +1,50 @@ +~$jy^tEWOvtI`Q0JJ*V>Q4ax66oJS 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�G~-HPJoE1tpGqGR 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

((RFOrT*#EBhNf8^|YyNR(9TA!LF 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%0A~TmisKAAL3!mL~(4I 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

qvKuPf!GKa&8!{9 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

(VzN8zn7B7A%48N61!$W6 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

kL8Y83P@ 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 z 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 +image/svg+xml1 + \ No newline at end of file diff --git a/plugin/card_game/resources/css/card-acces.svg b/plugin/card_game/resources/css/card-acces.svg new file mode 100644 index 000000000..13816983f --- /dev/null +++ b/plugin/card_game/resources/css/card-acces.svg @@ -0,0 +1,92 @@ + +image/svg+xml \ No newline at end of file diff --git a/plugin/card_game/resources/css/card-back.png b/plugin/card_game/resources/css/card-back.png new file mode 100644 index 0000000000000000000000000000000000000000..9b06762aef70cca0af542271d9c3f75ff190dfef GIT binary patch literal 38672 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 OeEu^$e>NYSKmHFhM-%M; literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/card1.jpg b/plugin/card_game/resources/css/card1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9287dcc156823829efe101341e1e30212a61120e GIT binary patch literal 15890 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 literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/card1.pdn b/plugin/card_game/resources/css/card1.pdn new file mode 100644 index 0000000000000000000000000000000000000000..91d6f0ce9e2ccfe24d0248c32cc08e7a089ae36f GIT binary patch literal 54964 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 a$He3R`^d;xP>5~%756{J{{MRuq5lKpv$=l& literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/cardgame.css b/plugin/card_game/resources/css/cardgame.css new file mode 100644 index 000000000..07115396e --- /dev/null +++ b/plugin/card_game/resources/css/cardgame.css @@ -0,0 +1,238 @@ +/** CSS used to show a 5x3 table of cells hiding a big image */ +#home-card { + overflow: hidden; +} + +.enjoy-cardgame-active { + position: relative; + left: 50%; + top: 90%; + width: 40px; + height: 40px; + margin-top: -35px; + margin-left: 10px; + background-image: url('card-acces-active.svg'); + background-color: transparent; + background-repeat: no-repeat; + background-position: center fixed; + -webkit-background-size: cover; + background-size: cover; + cursor: pointer; + z-index: 1000; +} + +.enjoy-cardgame { + position: relative; + left: 50%; + top: 90%; + width: 40px; + height: 40px; + margin-top: -35px; + margin-left: 10px; + background-image: url('card-acces.svg'); + background-color: transparent; + background-repeat: no-repeat; + background-position: center fixed; + -webkit-background-size: cover; + background-size: cover; + cursor: pointer; + z-index: 1000; +} + +.card-one { + position: absolute; + transform: rotate(160deg); + background-image: url('card1.jpg'); + background-position: center center; + left: 130%; + top: 50px; + width: 220px; + height: 330px; + margin-left: -110px; + background-color: #CECEF6; + border: solid 2px green; + cursor: pointer; + border-radius: 20px; + -webkit-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; + opacity: 0.1; +} + +.card-one-2 { + transform: rotate(0deg); + left: 50%; + opacity: 1; +} + +.cardgame-pack { + position: relative; + width: 100%; + height: 450px; + background-image: url('cardpack450.jpg'); + background-color: transparent; + background-position: -500px bottom; + -webkit-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} + +.cardgame-open { + background-position: 0px 0px; +} + +.linescissors { + position: absolute; + left: 60px; + top: 135px; + width: 220px; + height: 6px; + transform: rotate(-26deg); + border-top: dotted 3px gray; +} + +.messagePackDeck { + position: absolute; + text-align: center; + right: 20px; + top: 30px; + width: 200px; + padding: 20px; + font-size: 22px; + border-radius: 15px; + background-color: #8A0886; + color: white; +} + +.viewDeckBottom { + position: absolute; + left: 3px; + bottom: 5px; + width: 73px; + height: 60px; + background-image: url('galerie.png'); + background-color: transparent; + background-repeat: no-repeat; + background-position: center fixed; + -webkit-background-size: cover; + background-size: cover; + display: none; + cursor: pointer; +} + +.messagePackDeckBottom { + position: absolute; + text-align: center; + left: 85px; + bottom: 10px; + right: 20px; + padding: 14px; + font-size: 16px; + border-radius: 15px; + background-color: #8A0886; + color: white; +} + +.scissorsrightinit { + transform: rotate(160deg); + position: absolute; + left: 260px; + top: 50px; + width: 60px; + height: 60px; + background-image: url('scissorsright.gif'); + background-color: transparent; + background-repeat: no-repeat; + background-position: center fixed; + -webkit-background-size: cover; + background-size: cover; + cursor: pointer; + z-index: 1000; + -webkit-transition: all 3s ease-in-out; + transition: all 3s ease-in-out; +} + +.scissorsrightinitfinal { + left: 10px; + top: 170px; +} + +.puzzlecardone { + position: absolute; + display: none; + left: 5%; + top: 2%; + width: 90%; + height: 80%; + background-color: gray; + background-repeat: no-repeat; + background-position: center fixed; + -webkit-background-size: cover; + background-size: cover; + border: solid 1px green; + overflow: hidden; +} + +.puzzleMin { + float: left; + position: relative; + left: 10px; + top: 10px; + margin-right: 10px; + margin-bottom: 10px; + width: 150px; + height: 100px; + -webkit-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} + +.puzzleMinOther { + float: left; + position: relative; + left: 10px; + top: 10px; + margin-right: 10px; + margin-bottom: 10px; + width: 150px; + height: 100px; + background-image: url('b-back.jpg'); + background-repeat: no-repeat; + background-position: center center; + -webkit-background-size: cover; + background-size: cover; + display: none; + -webkit-transition: all 1s ease-in-out; + transition: all 1s ease-in-out; +} + +.pimg01 { + background-image: url('img01.jpg'); +} + +.pimg02 { + background-image: url('img02.jpg'); +} + +.pimg03 { + background-image: url('img03.jpg'); +} + +.pimg04 { + background-image: url('img04.jpg'); +} + +.puzzlepart1 { + position: relative; + border-right: dotted 3px green; + border-bottom: dotted 3px green; + float: left; + left: 0%; + top: 0%; + width: 20%; + height: 33.34%; + background-image: url('b-back.jpg'); + background-color: transparent; + background-position: center center; +} + +.puzzlepartstar { + background-image: url('animstar.png'); +} \ No newline at end of file diff --git a/plugin/card_game/resources/css/cardpack.jpg b/plugin/card_game/resources/css/cardpack.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04e4a56a4f1ad243f3713339dd90159cb38f29d4 GIT binary patch literal 58279 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≷9VmdM>sq? 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 literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/img01.jpg b/plugin/card_game/resources/css/img01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bbdf4ca609e29c0770e88bab059bfa2d254b33e0 GIT binary patch literal 205768 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�) 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

3Z^ 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�(h#;`=tAM6{T7hlz=W>1xns~=7cFp<~{wr>(4nW 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 W+dfjP#C>RVOOqG? literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/img02.jpg b/plugin/card_game/resources/css/img02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f4c153870c9014b6af2a8cc512af86617ba871e GIT binary patch literal 125390 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�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�~BGm)!XDL5t==q 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

l6U-!#xzPg2lML4LVk#Qt!18p9p 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

K{$0{n4JHojU5P#e#LMg8M_%Ht6mY6A2@6+-hu* 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

|?WknTq1mPFkh#G7ooREB9FlqT?sctZ z`HKvDpeNKE{{ZLTR5AxMWUR~PGvP#I0BThowsogu1Y{BjC;tF3s8YzX#5SCndwO%Y zcK(_!35g8S5sl=YduNV6pw)j_d5ku^%~4R&0USj0@7n-<&b~E`>(y!2Q-*EqK;=Lk zhx+|~y3R`TQs#{itZLl95a)EgVdAn_RwmZ9a51i2JfBhq+ox_KRr9j?L6qSZ45?Yhx{v##6Z`Y zCU^5{V3i1i-YFI`6kB_NDRw~Ua)1G_l;^Y!OJCoYhH0|jxM=fCHxsp3fh$Tjka{y@7y z=b!xJLR~!hM&ZLZZ1x(TU6+%Q%V1+U<51$uAvo_I$LKGg^IuEs<$^_+BED}#Yi#mpEo+PT^WXUih z!xj!h$k|-CJAMBEU1+L}8Z0vIQrsg9m->T1lbGWjqoZ)4q`j1O#bbNPE~G%~!5^Mb12%-I|j&LduZa58@5 zTj`-5TE@V9#Iuq1*1E=algrNs7{@&5fSf^|<#*NhLDUsBRvLAhc&XiEs0y4*D{!$d zxd#QkhCes8jyf6yr<$lrk;k>ACp++=OB3t3?m6Q^Ep~cZOPxhw+SLxk001I1$v7Ul zB(_Q9lb=DT_uHJ6wQyMNpkd$r;lpvA~$$9e;V; zW^lz&Mj1B@VFCW-uzrJ8^?jn@cf9-!3qiQbS-ilqNROT6l0U@bkT~t0<4(64wW+(w zN;j6No6nE|gS37D^c-hCx^S2d+MPOX zv}SehwzE1y&@1dcB`rmChG`*yz7h{7a056Xl0iRxa<5CxF`Hu+L> zyBrB2kZuRlDB$yvuA-OCpP}lhcP&(sQnY)FBzc649H=9cpQnFKaF>Xp-A#9?6c?%) zB#fwKjVc~c!_mt#FZa$hsah*B$eCAI%7ba08T&f;(N$)pj*fe!rka7(mD=Mc`@xeu z4Wx#_>^Hg7$B8$aS(YB6u*{Me)U4n)nG&hXZY-^{IRp*6k2u!^+j^n8YUY<3ddgWP zjl*JS_b4FuDtOOs2-ic{zi5juS6OLnb@rQmEpALR1v?Rtj5!$NaXjr&>U(Op=(jL* zgwt!l8*G+kyR6{8Q&!1uS!q?EOiIugmSeaE*iSeevy5jNqP$k|j$WF+U1F<@HN@0x z0x8#NU&8F*e8tYy2PAeUvC}_S+HR3sZPTShR;a3D^QoGk8KV({oa|CEKZV9eLs3gS69ie^OMv{{XD%YZ0xqH87WkoE2dt zj_2^WIAAbA$kc|jRa-0%bE=VQ_AmTeL?vMn;eLI*spHgr^+I@~HP*W6y1uELpeY*p zdrscxk=%WArPa5wtPBWy6{$gE9JK1n&)dFXQ315mr8C7+l@my^k1}8w7Tvpm#E$Ff zqWyd09k0f&j--OaUqmYdFq~D~76&sNxg;no-~rrr?dz}4nUJsI&QHFq?+N;Qsp+GS zs%T<$hM}ICDI4Wh!1=~~y$RKyPJwgrD^9Y$Bs zKHoW+y<2Nw9&yg93W-&-h2tk_I<`D#f+#9suAaK4;Zsunb5%9C(Zs}sEgWJ`oJc~6x|vlAfUVq}Gj#1b%Uw%IWdn4XQ;zxXr47e$ zBfq|ytdTWkbWuUOb^?aaY;sTZI&PE9O5UZX({}f$q^E)+?6FNLRNQi30k8+YHDJ;v zE%HxA0ez$53YGczVU_yiX=}s!QeujuVKF;NP#b__WAgFujz3*J*(8Wa42+H>+vbG> z_-xr5zv(1#>7F!uLlMLsI6TSrYf9ZIscI8>R&q9w12UdK`;YR|4ActL@+kRMc-Ayv zxLxRS4;{G#ep-`ljyffYWijE~<#I+c264x!8Q0qDr973PV}v1f1r9(2=l=fy)N3Bh zjLme$1c_Tv(oGyH+Y@+zf_Ws6eSJ7>YPyNy zL}jQlLSGN_D+BB5K|JVOmDFafCa3vB1!DWNxj8rk^wMs|FtSyB#&HEiFi25|ilH($ zY?C-OXhF)4Q^@|hTIuS_x#{JSX;LYR%Npcx?x*DY z94Y;DC#iaj)5A(2jDuDaE?XO8NU(VPhE9kTfXsnn?!b+`NpOni;gr`gB0SwScuzRv zfW&&7gMrUF zZ%Q_jBHEA}1jx0$NmV5|q}v3Mx!%h>5y_SV0E3LL9B|sklc|JAVtZ{VRT*8TL?OHG z{6~@L?VSysWCTF^f=1O=V>F3KDuu>ih(bVjqy6wa=233dY7l^?sQ5P zIAyzBTtOV{*=c;d5=a=@ImZ~sI&Y%!K~buy?G_4atW`B)YHCR2^CJEUZ<=H>E>{J% zE1plyw_MjpXzB}1Iu(!j2(H(NQnOcL*HDR`#<>GQBV8aUkx=x)iFmmpBfV- z8wXf-7?_Ok;Iqcc!FLiH7}rGf&s5s4boR@|3rTId%S{|oOD-$pmwrOURsj+$e=BWd zU~mq=cI_i-)Qkvv-1cRyS3wm;Gb}|4qXxhY<+&&2@5->oIr)L$gQ0a4%UMilY8Wi0 zQetv8<-fxxr#*=s_&S|!yt$?l)T_rc4=!jVs1meqi~$_P12j#7QP^j?vB=iTy&_8x ztbvqnPnJafR|h=j(}R)er0uL=#}SODZZp(cYbBs~^SgBG-AX%USYUmJKDYum`cx?M{N&f)qv)@?G?2UmOTrlQ7(sdQKTcp$wL0e<2t&ig4 z02@$gr;U4LPY%`~90R*{GpR?bZU%`V>Pni{G_T}}$hOHWe=bJ&+vS*Wa1I-s z2Im?Vc&ck?qP>eAK~%`G+By=uB@oKG~KM)aweEWiv!CoF#ydvl*qKDvh`>d>~zW`3Sg@MWE% zHIIcs{3%rAdJG@SL{E1R4tDbl-R<3Mm2~%NUXrV+jff6mwhFa~<0CBw@IdXKpw%_v zGgr~yD=0;(8+3JXi6M?kCWJ>ebLHTGR1yduTxrJfaHF@yRd2M|spF}FoKFJ$WaK}V zNnCAVo;dupv2*LrubMet(S5F`kVobNPc*yHM-PJT#BCv4=yR_?zKu7T3r=qn7OOM6;ojRdt3leQ*4t`KA6By^owx#u@(as+8!c%EUF18-nb*3 z2D!^YM^3*5Sv9@b9gBh@k&k>fN`v$}bMV)`*DdD4G{uUV-8ts0M>uXnu-W5mI_DX5!=pZ<-w`hX8_wz~uN3hG9@cx%@k9A+}5uDUwPU{?%@RTs_x z`pFZWVSoBvB0ihf1Kd0PBUCvpwsi@Lx~f1@5FlOIW5c)9{XW`>RJ&V;*cK-g?&vG)6D70z~}wbaQyK_9_q+~*v9VZMe_o zHp6Jv8Jd zx4-p76g5-BTU{c;@zXE`Q{@8#JdAUtDh`$EtF17ppt4mZGd3Q!c#JgYeawym;Clj3 z(_d84DN0Z`U=Qs)>ZbBYm{eAhO27dl->@F%{WWuR7wn_f9b3>A?-sg?#14t+4w$+b z8Y-LZ$>EvcR$wFuQ*Ioo^4BD=!2_Krcy}L%-78ToXHoRTU3GUx29lPpsp7IkMsm|e zn3k~T7)+vf-I2z(c%RnYZQ`BTE}d(8lC|k$^62RiL{wBU4i-9UbQ>W+001b@10?9? zyKZf@oDV0@@c#gq&2?8$%Z$e>hlwAv--L5bLE%43*Z7HYj|y)+P7O7nIR12^OiNeK zBhF-ugZK`re-eCL@k_=U$ZC8*>nn}rDFI4KHrWjwEPvpJnjo=8I0J>x10?DlErib@ zUo1(-1x8Qw*7}=Rpc~enK%<H9SdXqkX{U=Ns_eqT*jmimg`5h*2KT;TFPneCl4+OPKr43JCAPs>oZYe zuPrrnwX>gSj2#O?C6+kmq%g$WNi7eSq9AZv2j<712a}%M>!bQhr>)Y_o84+GaMP)Z zkvy`wC%E?~9G`RfX?w%(3S+TaW30NE<*AlGiK&pW`Md3m7UyX9IM07wSQg?SovLc; zVT3@qKwKnra3~o+^D`eo=mxNDyK&6hL;zq+Z~Y%{k`|RB+VeK%037GgW7F;T)j!p7 zS6bwT6mKb}k(*|Fpx}RAPOa{*@lT_APJ!mST%#~kCz#~80G|Ah&|_3J&&Ixl>uQSG zD`a)3i_0z{fA~?hNLFFkoT`Ofu`Pj)`k_Z7a)?xQQ-_=Sd&HW)pQ5cwd8H;-A>Vh) zpIm3Z{f32ghl#IUDWI50vPoM_Z>1Z-G8`$F?}FJU{#^#T*4WL)du<$#HB=BaeDR{~ ziWLe>aG!F=B!TET)fd+NN@q6Okj%3!y0YStk+g!!LchLF&(ltQKTI6qh9a8%N2WI{ zVYT(7RsC(%{Zj;zR8-c^^pr(g!+%=(}oW5Q^sZI z7*m;ium}A>YkIW9oDQZ9mSqM{aLWvXyYHcpt0g)k0&-N0gOTbr1?_ZxOChE-w+KSCkRAl9+&Y87&$!bd7C7@2CuoZg zQXz;zDrX+!kbl!cra5BDeq8>VCvlAyRwSHckG6%#6^`XTzoxl{IFh{cFysu4d?X$_ zYb~d41`dC|u=mD?nQ5{Krh-lI$jEzSYbSJ^mMXD1>=a=S<>{?Ta1OpP2e9W;nZQq~ zgq$$J#!uT><1e|m!5Z{!#v71$`j57@`2Za5+`Mu>^S-+u6SMOe`gW|)ZK`JeG@L2* zM99hek-*nj{>C1Bms*RpKq6a>Q_D@BLZo0RIQo|0{WZswQg5n`okrt;z5R2p!~LGG zbX_q}8JXRwX<&jSP9xtV{HdREa!4MUM_0$mEj}RCRQ2@-K~V&#si>RGtCb`KDw0mc1GycE*J4?% zklQWLQAB_QP!2s$uCqfDcFjh@^EN2Vn8r9d-3(}9^D)OGCs`VPcK|cawLHF5GHu7o zGDo(jX%d9zsfA4O(#M9*8&vm;w+pqL+wXSC6vYind}Mobz9YfM+g44+v{hT1ABjl( z#;bm-t7^JwZMCo#ihR?vckVt?KBGgmfLP57?&dFFZ#KG{BHV2f#ZZ+E&Pd9W+z@?r zUiCXyRoUjErlS#wk(9ZM_+(=FDTYWf-ORM#79o>ju#@k<*5 zJF)}nM8j}~cU0UYvfk`dQC;GLY;{n4 zzY!2VjyRAHW61hxe&5l3Nzj*8g3(fHt(D*I^HNo&N-*p}BOvRrLEJiY~cf%68N8Tw=ajaY3>HKHeq)kjSeMm|K^IR60C{(AL>d!-spZM!2tuGmQ$ zGRcpwIbXQrN2z*sRzV4`!?p_MJcj(warM=fvg0SXyV$usu4(E|9508qxOBx3T0fR=EMy4=0YCDN`T-#L+ZxYhq z?`9N(A<`q3W*lqu8=)+=Zx5{LNks%|>NzSjUtM(Igyh2MKAhG!sc69PK4yLy_)BH* z*Qe>|qNj>E5db&gFRlFA%ocB}iX*`>)wIUw=|h~DdwraMmnpG_!SD_sG6jC01JA9N!o*ebqt zH9$65kEuG6lEp}-P2f9`$@Cf@BZB_`5g^>eanEgFDl!a&9x;sRkoXWqa%A0yKM)QM zbKloXcSx!ykh9Eo=Ouo+GHbb5dE-63^e&?lmq5fE6M^ZWIoxH{NP+6$MHrR%z$2Ya zv&gK*9R3^}Yb3PEv@-(0{{S<0@2`>3y*+N|&IhO(s2fi(huk=a;*cY7F}sd5$JT&OB3R!Snz}fP%ugUn#nH0 zbS5?eL^vS({{W`6)JacPxF3jdp2U5QgVjeQ4rYo{?w!XUI{yH!sx0}PvNBU$1j8qS z7^(Z~I?C0G2&z?w$tYv=1mOPwmXp%p@y@jc$P(l@_F>rQqFlvQ6p_Z?D=TgT*Z%+x ztmh*#0-WL-6<%FbDj0q?JP)WkmtAxKMH?>Eh>2NG8@_Lpaqd|2_V&`k+*Rr$m6-gh z2=v{Nty5hksB~8RQq&|tk0C(MKR^M{9PNZu%x{XKqFE|F6_F=&F(bM8BHi+WbL)Uk zJ$TXjyTmjQ#|;6D5<)!kDGCU{^c?-QUD5R|HNtaSA`uyoLrkhL&QHi&>UM%N>8TTS zbaP%RgjBFJG|sfqnNg1UcI55nvizW*e4STt#FB)brG-yl955IuCTV4nz^bSx3`xP- zKT*K{04)@x#X5XVRPfWOA$L1+>^%WIpRmYEU-4k0b685=PVZ|EPDg= z*R9=AU2&2++l(&`)x;irpvXDkZC)2Y>8kpF~!d%OMWNExjH9J)waq4 zv~o(v%S`NsB4UgWzx9)b)}l+PX2w?rrxE-$klMnecNt-L#T~eO6nwrK~qYE zlJc%&3^^N_j_`jk&ry>s7Xz4CEgp42sH!GpX3F_-Y*UO3Wsf+|em>gKa;1_iEfhNo zumo=mgnDqNC-l^1xK=UqQKMiKGe(lCpw0-u{9pKW>919hyC{Uyw2F40AiCh5*&Jsd zo{5YJUP*(cDJ4UzGraHop>d(IL?sdhBjw0o5A@Wk`Wo8$gs6hxsYr+CNZ@}k=f3=& zKTKm#=c>2U0Si>BVaRRdE(ieNgZCP+C(P^!61FTMX5)@^2O!hDiVxtzfHHfJ`)K`b zz3M1KN6W}YayS{zfAjCC3r9&u6jaRU?IAIs&vJj8@;E3h(qQDO_`+O`=>&n{r;PR=8M_W}1S8NmH#>@dxj!7fXa(inVDkZL! zq>entOA1o2nA^XKPtBbB05kawL5>=Wt;SpY(grc1iY9ZFUU_e?sVBbxY9;#ZPimrA zDdcs6nl^px2r{ubX2<S>T2qLjZi$eBU7I(Tk*Ni zxH!@^G{PFlB&$}IC{7G({Nn&0pdWoXrEEUvhNPBUPWtHE(_JkL@KSs|JN^yR2|U1i zo%uX|dgAXKY_@)z@fP`Pl9noXA(%@|3~YljxD1$3!vhBZ{SSR!J`Q!wC1XWRTUSvb z7*to2%vEAI9e_{=U;z4OzK8zQT@mq=E#f5=uj3vFp`oaY8wy3aibm7Ah?+RwN%^7t&HqFYWy2Z5 z_^GPimQ9hmeC0slMmWxU52tgWw1Oq5X-meVa0W)_@&I$A2HL*Z8*7TbN! zvhcsejg$TYqsm4(R0NQn$3EI2(mp5IZ50u)p^Bzi_N&V*m}v?8J5jhBeg6Qqm1*gz zC+MS5X@To5D4V5`qAu$mK*k9x8v8Cu$0xotsZBLaB}+{+hPcasg)kNXJr5WMM7FT` zQz=c%q%b^*)E*u9uWY628Lao@w9#5?b`LIXqyHInZwy{1@qduCI=x zrJk16MDffiS~?(Qs+VI)lp%)nZKsekySA!JRW#Syc`EMr_@ekYOjL;~g;`vlFhKkA zM>-(BB6W91^ySN=>iz<#R)urbZP3 zp9Cn!azX8`2kKfYiZrz~^c68wO8A$|jBE;d3=eR`=Z|eLbZ?6nj+E)i`Y!1M)mi14 zrh!iq(M-eUXwUdtg2;F}4z9PdtXJGG)9$LPe|f+&?~GN5{ZCU-YmvEL5N@}8JaIV2ijAs}><*#*xG?rS4V4|gJ*ib`Lh}#Ar$U`vC zxg3p0eEcO!OFGGi+@(|yK+Zor55BVX9_7r@rM8`>4!B$!$&N9&Z{1PB$IZaU_v1k# znolNKT2iGQfKWFLq~IKVIXc&Pxg>QFO)RQI#s<;dPdl^y^tC1OSPW7mm}sfaLXdu8 zpXrTe0lF~nwo1O;q4@f#_w7nxB2k>+aoG0v);6CmIOrBpBKbo&D}bz{B>j0m+dwxO zjgUy?{MBR6u1Fal(;9HMP)jV7?(q`9ov02AoPEN~g{ zM&?`-tA@o(S9Q73R3MToZ3|4nQBL_;BVxlo`@Ubl8iKtOTGiC_O-m!v%2h;0U>D_J zLN<9}kWSw2+_A!{>Dp?;9Q+6?EXo1;{{V@@9b?k<7TWntRLvYt#z?`aNj9r|${3*` zn`sS^>COg5l4ga2;ek_ta7R4yHQ635bT7wVsw$c*f1OiLJoCtutFoCy!93PtOFr0< z!1ZU{fq|3Cx2ru>8!yDt%NF$9f<)Nma5j=VkCd)6?lj9NJw;WBI5N-Z3V74QYkD9> zo_ea9qaY-on1u;}{{SlTKDwx`xB82TpvCiY4(r<>UrWaYMkC!TE_=AE$9gn!zL3l^i z*M6|KS9JBNiZ{L3ZM8SsRU*_pX0;c}H59PQggjCh{Kfzr7AkN`^Uo`i2XZ^(R(I^% z)U@3x<5y2q*Qc37T4%RZjD;a5iIX6c=t`fI#7B(KZ%nxht6Gk0@D3M%ph_w<~YOQP+oxu|(-M@ZT$NHz@f*Q^Cgs zVDqjL{inJ_^p}XU#bm6W8mX(Pp=FJN?R2Dx7De00Y%eFi2>R=)JSMBX+B!1HMb$La z@>fOW$JPc*u`^|Xsd)(bl|LrRFU81 z^!7O%kNQrjZoQ?p*sn6p9M2URy0K|v%1Z>f2j*Vhpz+^VCsva!wJWKk4e;VQk(1ZQ&#l+5UiplmZFPk{w~5li55E?Fjro4$JbaFfE$^ht&zm} zZK(;BS!r43i@2@nERRV_yyOo?|9qPb`L%=9O8V7Zo z%d4Tejms3Ud8@RHuNWawcgsPCN#!b=&$u53mZ2M#$eVO2R z;Z6qw*2?hSs+BrY%#;+!l!Dz#)aw}9jTwnCv~~kU;=ILGah91I5)J7TW1hzhe)<0Z zEqSNbJCfzdCx~SMMlnAnKWV=VX*vVMzN_i0R4WYxRfXsfv2QIg=0>}TBw!X^N#h!& z?bip`XlIl7RUic|*;sxObLpNxJ#}C0M^i=Bo-Ot4RDgN3)hFN^cM?xc0(r54>P{bz zbUNf}lmcADz(*-EMUFO;=r!y#<-G)E?*eA8xRsCHCDuJlNm)f@qpF>lN#)bV*>5bd z`GT}S3`5T)KpSS0|T@wp&o;Pqt{B`YrVdnI07wwZR!jTB%2K_ zJhc>d>gtIg__Z^$M4+-B>EHfPPCnYO?AA!dinFI~7kCzU=V`6=uyf|447_s3--FIR zyd6}ul079vQ-a^YWqy17r}}ZHx?P2uApZa+a;>`u027c$V}LbER}Y-T&8?=eIOz8Q zUtzk?)IAl-2+ObGl@8#*a0vQ+JB@Vz0RI4kx|X)0@#0GYzDrxI(Ek7pvP-p8-~sM% z$Lp?kp02&7_-e^kt_EDfBwV*Xn}!HKo|}4G#ICmR*QGj>ruqWs`=IqJ)Bg0XfbOXg zL=Mb=e9QNuPMp_4exOyfIS{{Y3NTi+T8JbttFzk=63yPjKZs=mh!MLovb@mo|@Dia>U z-#f=5m1G#?k~Z=aIo?iiJ-H|B zG+N3-XE%aGBiiM~-GZWMd{wYH|ZSX`>N@e`HW z+0U^a<6fB)9SHWCg+aZ$jImmxl1=bN6_@c4Kd;kYYbaJ><;us*L&nwlhtQ8*3bfzj zl4OcPQlR8^BR=CyMhR-!UYv(H#*(Xg5Jx|s zrkbRos=Y}JR||mya8Vpl?#KCO>!HsLd|bj>H^>YmT!J+|g0bmj4Rl}_94U$dp@*k) zrVYD>L4aCzo@3>wqil5a=pmsp$})2DU^99Gd;Rqex5J7k0?tTNPLZ%SD3N2!2E)XZgERWV_ktb-2XXg632Q&-=Jwb42h68W>a$yZ^*FR)SEpQ$=o@Zr%dwByM#Qv6Au&2Apy zQge;IxX*8*vDR1W*=uH$g(Wgo0XSDU!6)1fbzW9=6x6p#sUD_RWr)*8z~N~kBlv|> zak!6{KV4{7SSjhO&$iMm^$h462uqxI3#AQ0EJ;lwHA;RKf5Pqw?l&<2{WVwk)k|M( z>j&x6K_gsOrxpMe2<7H6DI@$L3H->^i$7Mu7e~cssWPKpYbkca85^Ou)3}g>?Yn3g z^(1N+TJ_D!{{Z53Usp7W$sGkHnaYf;t{F>(KEcK_^gjBnDAm|le+f5SwpIgi)JwN@ zak1U!u*D*ZdP5Wp`+K}Z0#DO$Is52OQeGi9`k5)}L%>vpW%N1Vk@;gvX6lix+E z@6^}GVxqf)O*B~Yst@6hJ;p~NNjb--rh!^(57-~^Ya7^z-(lV+;+~n$i`%$ zA_`oBLG{)IV32!_JoT@Jw+^1PQ+0Ksx*OL@++CKA?@jGhX;QksEEu1Yq|X(4*ArH6>1erld-qVa}AMj((tn&Lhf+ zUTHqcr2hb1V^jqcT%ZiA>+B--?81c<Ad5 zm(R9R+arx9PC=?~#h<7LpT56A=%x}l#twDJzp z>#glLh9K;?;1V;Q`oR`&a^Lrfx)7>!{{V9cN~9c>#&j00IU{+92V{d89{N1{WPmrQ zJ@pQzpUsJg131x~d`ec>?j+aK)zZU=i2^m;KEC=7bF`Yva?B#ix6RLRd-3_|#5AhG zQbY_k_Bw@AZ-`ULOt6i(p=B3t$`$ z;e&ZII;#wIFnoQ!NZCmaV32MDKB`A!sB_u>0DDuq%M&Z-`G{~$J~o2zN3GF4SX`$;3q7!IFMbW8v`l*v5i3!2qk8l@d-tn742AF)x29L~^Sk`693d{%Pq;8<=YKl6uPaUzQQV3QLfKUBi znt}lCSQc(1x{4E8;LBS)P6+=1g#2q8bCCdK*{-t?Q)Z@e|xe#>xMv)w$HErLX;Ya!E zTzxar9XNg>j)szsL>t~kZMpiL+J)5j8aq{cMQ*29(K4*c4<}U>?$6g9b8&NXs21xS z1d`|ILmm>o!EIy!?-PHAG%IbJ#jWLMf87e*a)pz0#iFTfjrCUWEcJ|LJ zgT05YNYt9kpGtIfqDn5ExHR>_M)sXHD;#;DWS45WSip%R8o~`D8!BF-&s^#%#J=HoRUWFN*T7F;-c?PJAyQxqjIPrb2AFA;H@guw$fBnO6pmv zWRMll0asREgWWkpHTIX|84u3BgWpzXRQP4#W!I!?dYhvvmYR6fABNs{7AM>3p#Buv zpt8&H_XQibe1*riuR5leT9zvi2qVFadSz@1c@y*#!$@uPR(K(!RRAdDagH>{bELtk|(CG91%8vAmAQJ)T&{; z4tUgPATz$^2^a(oO}S37u~6A& zW6pH7RArW?B?l_m&Yht#jKNshh3I1343jZzu5qZ<&aj6dfTJF|^!14nY*xc#K&zUv zvQHu)aKr?#yG~KQqipvD@fxbo)50R{8-xV_*~~3 zkM-4l*=^aGJ$o=Qso-!4`s*4~zGoZ)4n~8?aDq8XqX2y|>#V8nViL?qE1vo1LN;>> zD~9B$NM{MdWw^(tpSn`G(%V6FZLEI~^wl!AH?OYpqXfG?2H#CwJtb?cr?#Wjau{%! z^wQS9lNdw-GK-YV3#)HHGmvms>#wbRjx<48B5aU=lJ4U_e%cv59Bkp^GD5?%D|bHH z)o|(S$HKFI?|&{~mJFgalzo_vexqEL@i~$go^_NnM8j}DFe6bZZgEE{kob;p*ie6) z9O*uas_AQ-(zI_iCgQQmWkT?sxhWvdeKRB!_TrL%cNZ^H@H3&gx45-8}&;}!qT`t`(uM9IHv~i@eB|Kr=4d<3&^&ct!0P1xT zdVx=DhMF1CYbzIQaFoxKtaq1T*fHSpdt=`~%QP;~yvB-J*>5v1n}mfW5$#j9Fvq$N z@jl-D@G>)=&Q|Nyx`Y{Er4zY*qG?;pO{2I>bF_cU`s%%cN=sD>%s`e{6A`01`F8*@ zlfW&4Go0f*bFC0oNp`Dgf=1D9^5bPJ!LjX}DyTl$Cr40FaR$UUa?u@4S6eZHqW9#_ zw-G~47V{GQ&T@G5;Ckm zNR4FLt@UBu&wk{Aq1An9G}Tdws$wL@K5A0(NWVa%jD3gasceZ*Gt{%;za^TCEiG}I z<;s}~#0MSzP)X0RZDU2%(_I`awGh;i*`ikpp!dTZ1IDY$3fS&1EOfVu$O`I zRDd}tAj+`qz&dNTQrt{oYVUGgqE#TxRpoh#z1WZj82Wv2>8mQS+nSaPLXH^V5PgRj(v{ZbHFe7rsIZvhF(oZS zx`4fdkf$8?Byu$hqW51b$VH+#<3JLh60E={01_K(4CLf|-=3;njO9=SXT5c>>C1IZ z-le3ap|wKQRT36bBTxy+V#Svu)04>14vX>HKBA|igR80pZ8er|p{l7YzY9Y`Dp^RB z5Ks~2l?=sL`ecl2pTBBet=&gO&=zg5)?A@C3bhQ;#T7cZ#FB;pj#QE9!NzzRR_aUC z-6dkKsH3(aa#HV zncCy6WfotGsI@f(Oze#G%R0^#ShEeRrwqjgm+--UB$t zVTE8lNh8}_ci`WMv)FnPs9vsL9Y+nu8oGw4m5hf9&s5>qAD=R(bArU=fDW&YkLyY+ z?W&upsIIX`MQ{wrCn!N*g^?090f`uP3`c*Ef!{^8hN;eajA`{rs8C06r5#f*P_i|` zPd0joo)lrrM#uPTj^wW+2iNPTi(4uvjxhLb&#}e`=bVpyRFpkuY`xY~Qu(VDeLBTc zBIL#8iV^qpT73jpp2L)##S}?V>mvfa&mj=6RRs>Up~IL`H2#3SoZDnT;pnS z*m0Be)rne>nzf>(hBeG)Mv=$h9N@9-&m3b_M^im5Y?9Q&9IZ?kBD{wM+x}Jh<3DX0 zqE8T3;0`5Qb$YtWx2l|mc_b~g3@U$xKt9?S)CnHin%9w$oRXm6$&Ww}*Vj$;ln$@_ zXckN!7iTBb{KK&4Luz4yHzr$P$WlQyFOYoz&u`0GRd|_p=gc+GeMfVruBVowq*GE; z4UxbQqGXOfZ&Ue_eQ~a->AC2-qovI)+MYV9I+*;Yj&%%^t_E^Fv)f#8MSFXDtguki zhEg)jNs+njxSV71)z{(Ih!?7<DOC?CSz{vv;v)U2knucg6+Ffzaoh`GX?$#$#UP*TF<%Ype-Y;KX`JPaxPCxT0RX|9p& z^mOL3OFVTIlc|nL>QgHUV+XSDwzsx#xAY%QU(%|&;WxlVMMhSb zltMi6M*%?EJNlA)o^hdfe*ije<3&kvwcMJH++dzc$>$?@3ddt}!iL)2&pFPVr|{yw zVBJ4&lx$>3RpiJoGbthQ{{Se-`;nqGJ{;UCTHj@sN{MZ86&R68<&ftMbvdwU&b z-K7NkTN6^9Rz?|fDeIr?^98C3%G!mQp^lkrDN$TBl14@sBLnfV`9Snib&bQo<4Jjr z&oxaHO*E?*c+}yO$qei>^&tWEVs+4Q^z_zxhU$*2yLr{sX-`o+xcMP?m4HJ92cet=F8!3s%ye=$?a)zNKx}t5Q!5wy=Cq zKm+_hB{p;4CxOp=YTB@PWl?4k+#yM0n50v{wJIa{s3IX|i)s84E>As)+uK%WMRey$ z*e;hjDd313!jHtD5hDD+pQt;V?sUO@G!WS)y2CfbE2xH)Zv?E3vp6T*peOIDwXCEC zgj!ULm?k@n6&A;)r*-onc~W@6`H{y5aPA2hI?&e|YEGX@nMim|F5pP{wka$K{Lks7 zditu1Y}UHEXEBP3Xrhu;Z{}8FhEIH}k(21UgI#{5xOAn?3PjmG03;%uC_?NQFZI;2 zh8dCSY&eqZu2gbT!&kXKi3nFxyI*L>`5h-!+$o^1j?p$!I#VLa{B23AkdV@aag6X;Mt`W!=c87;8~Hgd z(Y%bWmVcCBdwuk}RC^Ly%${a8I$g~i)1hezjHG-F06-b{90GM^Tj-`$jFSFjg+c%U zQ}UDf9DbS$VYNVJjED(JX#kgS<(qE<^V~733W|g#Yov0~Q&UKbfC*E;B}cwDAIJ|~ zFtr@c4hYN_VY3E~o`BNFG;{fFIL1SyOiHP5`tWh|#-A+~2`bo!h)B-QDRQ~~0800}Z255FL2 ziC!Br`543_Y_n6^t}wqm6c2>$?&D-yEc2aJUPkbOOI zqm<#isR0VFwZi0fZu!UMtv3Sj(9Jm{%vHd^$!5s_k*-tmqrzz|x4X4>#U)hJtaUX`qhd)TG>*uG zjvHpv?UJenHQgkLmZ|9(8ev$lEQI7P3aem!JiLCot^8N%S~|)ZE>zUsc%Y)X+-o5N z=ay3xalki!DMK5#9f%(Kx}JH0uyK(;HY@0aQ%;K@Zzficb`E$r86KI&pmQ0+FFyL} zzZEI!F(1{)kSMG`0(;A=Ws*=VaEt*0c*O(M-62lFo@`i&W_ z=*z`1DrhPLvoDsSGDL{OhF0v{ow)CuV>!_O0NmBI?(Wpm{{XwO4tA0efx!pZgXncY zrA)DKMC$L_S{Le0u<4$rwlQ&df`+uq7s}rfDO}9i9IFOQW4Ft$fSRDSmd!YEBT&-A zJhP^7+ZQI$6wPt#mbd{DhtR`jAz6S%a|H9Yb%?hP}^Gb!V-+aX+ff_2!PF;Pno zLN!>Q_=3ipRzeFnj+Y-W^gkl!^3^ph#wSyfa|ug0YkaixxmjU%rI|)f2p@~;2r2sN zrMgtjYreyGq>!auB^({KJ_hnLtj@NI|PoRmO7Y+s6JF{kI`6uTCQ(Z zD_1ntAH(7;`1c%V^Wf_e?no0tv&1QJdI{u;?QeoOXzL`FSBX>;Pa}NA26@0Gi28lB z8f1ATNP??nmE9Nsq!eUt$c8_d8q)(kASmRs7x^W?!61{fzkRvJv6{2XNU9^0EQTT@ zd}k*I3Vyi*_0%O~^(CwWxjw686f|jA%vPAoN#-!X;0Yr+{M#q$eY6%!wM;ahyMxGC zw@U&A&KXm3l_S$Dfzo2dp-%4^JdjE`*OeGarMq-Q23_i=#@-5G0bH8MnZQkbJV)bkXCunOLw5s{wRQL5*ty2`SP zr>U)!#G2ao1za~VrqYal=QtWPCx&q}O3Sh|IBtIcV%`4P<4Lr=4cC2TZo74tP4vaW zD#+BT>gF<6JD6=oVpUGw=NdJIYFxR%i941%R(_g($URp-QU1@J4O)|V zwN~46+2;YW!b&GFY&rEY#}oem>(+ih-}=hK;5SwE2T0s%ZFP%L9EPG07Fb}GqY_F8 z&&&Z;&!*$5d;b8z)70=cM0FoRnG_m3gjbqK*}-uYLI~t*NtauE@8Ju*HK(|0>hrv#C%js2j! zHH*Su5H7tZ9eUPLfgM#O#!b)gvy3Q{{&i#k`|y2r%@^p~_mj&R1<{H)&pBQR&c2nV z)K{z1t<`;|WIb(Fr4M2VWeR+Z2j4vC zwTc*;x;K(CRY{*6h60v7`}Wj75WX`A+#VU~CYqgb8_6M1PbxPaGo#k(z4Ua`la0}; zcJ5c@{{SJR`*hpsopa1jyOYk==RdA={n1tn+3Bi|wVs3j0I?R@mZXA79dSKmfSBV* z$=vENU5FSPK+ifwm{v|^E~R)U;xQUGSQC0@@(x?=4@T_IS$o~K_9DM@wk>5{UXW@5KbY!yh_g7gfZIx9Np+t2G z3uS@8%H$FC2OnKHcmZ$mCePAUy*c4s>dR>BJN-O$R5M+o%|7V~^CK(+0U?Zlj6neU zam9g4(Vw+%Q`jqdFZPM)&YP)`E%h+idXk#s2)xHp8u`eCLJHKt#WpZ-jG?j*7&%O) z_Im>lY3uyWs=k;tbc+)^zo#B2=T;zv=C@TdMAYulq=^>w%91d{Adk4z0<)~HBxFFm zlY&9}AAMe)DfnB}odsJQeN$(sqy6CssHU8Jt1v%{w`K(L4?3tWG}E9lmoA&JwaGX- z($nh};g#w?>pczWTDT$)qLpNmcbLEq+lF}h>9W~=FvP1S3hm&Ie?2bHn^?qi#pO$r z^U3os+asM3g5Mkp)0JZxUE_Hw8|-w`ZDAay+@EQWR_nDb!9p6?$xtxO$;a0?&W%M$ zL|7!ThZ#SG@%iJvs)}2zkWOk6oI}KQNPN=6g!oLbkv5}XKFu{5C$2wBw zbo*wWo$G(Uxnp$SK`qGB)qGs$og2(S7!oV`V+=SyuC9)gyhBJ*$nvcvSMec*TWd-??r&92M<+=2 zcX}nOZl=1{6C4pVQAX?nf=)uH{!z%zIc_q2HBeu=+M){fs+p3esm}2kCj;AY^vAb< zwuO$ls;G)7uRzusX5CJp0ELOj-Si#xvsRE+pbqDszv^o5wp)-4KkotD%O>??@dlG5rhUR|4 zNwJWsI{-8Jo_(}Rzp4@&eMvZyq0`BHJ^%n`U_QVA2_J1$>MGkrJ$D9D%wQ|uS4|4M zFw?8d?X?`_swm(E9fyBUuA%%2*H+5_8@$TV0=exfp~vUBw;cy(OO?e8uBecw#LVZ7 zss=zG(~q{cN7T_$O$}UdmK4f-n2)Ptf`7Tw!EwU(5H3$G)R})u<9w{Cft*7|h8TjL z1rIH2tPnd_e>_^*Lr9UZL z4EDyoZCWRE2MWqL+c3!r-&`Iw(LHnfDPQ_>*Lc13=ST}Bj&DBfBD%)HwiO!YH=l#R<`G7W7RE7qA zTrfHJ_0~GRZj_z|uT)*S0=K6qx?%6g+4l=gi6MxY%aKQQ!HR-F>tDUT+elyqek~IJjABdi>jy13q$9(Ht zkvvY(x)o*MgRhvzoCY5N!dCejIaP;pOoaXNDM`so%1QlDncPq?$Jl z)iO^sjr`8skPojoU;R4icn6ktFTba@s9yqU6Qg<}(_u5D^${QwI*>!l-Nzn)ldIm; z;&_C-eAzww>&{iw^i7aCgv*G+C?6>YUs1CfkD>3Vb#BV*$Uj``6{!Uj4(s1cZt(-N zi7IS_6EK+h9MFgRc_pM3?rPN@q;kCj^|pG`>~WUqLY zZPv1?)6^9YD=WiM^F+!{bLtMYLw26N=4q%dO-V14=6Pq8nKOdJKA?_9vFdm#C9kCj zSz<}ZBzotNKH80N>3FN_ph{Ux6-7*xva4Wj{$=|gZ6RiN98DDi12EOT-BOZBcPn{= zJE`T|r23qCAEu&Q>+jZDq-vU_wzp6_+p}+uc2?z8B~S|E(>~e*)Hk>-?pENfL6zLn zWUOvIPDdKJ10LyLaXd*kE6s|{PSJcdJP#Si%-<;ebfbUknwF*V#T;y`-yo)t43D`w zTk4tWr4Jem}$s(r<|un|Dswr)Qp)c*1<-$+HL3p2J-I*T1yi zP}jy}vfezXaB)iuGJw9vI-{$CgM)?ESXV+D$Rn*kEmRSoDdCAi(w%(qGGH1Jri2?az zR3LrJYYKjzO1IfV{6}+?Sn>0H263%`%BzJK6CIV^vfP*wimF)TRgFe{$nEvhotDnh zJT-N-iqWy;DBM8$4%&f9U}`BII_pg8z#NGITxfODaTZpBvuOjABGbv*8vcc!LZO8->X92b>>WSMMX1Je=oNPgHoBMbZT&iReH5Q@Eb|*uy$w?aFI`g%(5y*Bs z$i}x^>O7qCG`ls)w{2GEO5|oVpyM-b?pPzIa4=3aRbQu*bd0AMBTv^Ck>O@+k&RcK zOHif0)nY{fw*i6arZ=dgu1u6hBzDG=yGjSSV|D6JO)8FsHIP><)u>B` z;Yj3jtJaUJF#;n-S8C}>du;@6WJncTIpbR(sYOrtn84tDHJjT1mosD*Z|)^Aiv~i>8lO; zrbZ!LGiN!}daCHI5E$l!IKja_pHcPD^2&DzP;>ah!JNILOL5RA+h?z>huKC)6H&J#+%kN7Pn3Jxmt~ z$#~V_S>)KBU;~B=oRCIw#;!J4GCPu6ZyqGhx2j5LEfoxgK$A{F2I2P{0&(mK{d6YH zRMqzxZj#kHm}$}}=L%Q}3vMcd9J%>dw+f@Tt`}>ZcQ~y=CyJyok)!gZ8NrN`pQkzd zfvU=@u2S6`G^t$k))#ytRN^Sz&!%~1100`z*yl&8g5v-{S*R7BW$|&k+u@?CwY$Sn zB#a(7XJ+z<8$zpX`IsM=k_I>M8dulgvgkuK2>D;IAhqCj*Tl-+V($FqVeZ6~?AJg-MAd zsHk&2QmXP^Rpj;uXdn#xoiv(#$7qUMsr3EEUDnErRc$`d(c7R_I3s%> zrsMY0HM$#x9B0a`vdaS&{vAGJ$o~MFBAyZtb#XV0>q^j$H&?5&=z8`iztImjYgi0>3H%492UFy81o?A=C z%67bw%q0g0oG>HnfI!yDzMu@S!%a)%7$h@)78Fl0#|ycbe7HP@A1No3s*$>$NHIMk zRwQu+9&**0LyvY-hR^*=_9t7d-EBI+U277iHU|VsugTznoE}H=pI+LlRm8BkXPITv zbq(GtZA8?Rv3x|5f4tMp0l-2?E>k5*Cy$pI1f43>^_|ukWl8BR^dzt?G&C_I#0M;0 zg7TvS8SV%?kVdHfx$)-lV(VyV>1ssOu+ud?Qi%MvbdakkI6sO!e5dP!omf`i5GZ<% z+Ls8;JzY?Q86!Ly;Rogb_v3?+>!7_4usG^gt!-90tac?g;~!8*dht&0HO9TF;CQI1 z)uV9cbLE2^dw>AO3lc!U426p(rRZky@sdS@=M-$Rg)I^E_0zs8L41h*GJxA9`)wMLyP*70G8qFQC%y&)9@b5qd4SyL@c^pw&0a6}A32q6G` z(g6{2RGwJm_d03tm&Ac*y~EKnP}9#vba_nXQX^)pX!gbdkkS^~qv^;e9>Y*AwE{bgQb=TH1eE~bmp-KT?sKL#hC5G24!a(v4yTbj zS{{~yYRLMTDrJV2nG%{P9sw+$6726GEx~2wlzJTVj>g34&Y@dvSe~9~W{PNx?&Ou) zsTfd!JE0|j{Jr&4bgx%SdFeY1RNxq@Ezv_TRxI(<#!9i=q!n$$`u#DZ^?gun(Zx_| zH|A0T$P}eU9lx1*RRKm0amEjGr9Q^md{0IkSfPiRa>Lb-S4L>+YFPR3q^jG8@WwDk zMDHJ@NPbbenDJxb8MXRcoT8dYHtJD1tT& zRS98*$;rX{AAJ(Fb!9`_>uxebQB2_?%sxtnP}w_uxa8xLomkc$K(}vFeAV?8LL%(S zkclP2Iojm#KTZhr8XrGU#LTq9pkz0fAy>(14`t)Nq*dK1q@={@lvOtEXyOUnVq+ zu~NzkH$R{`9rg0lM>Qmm6-QFFB1GF=DdH^o#&8!SZ|9EMWx2;w1dkKcGM0=65>9-u zJxc?%jB+9_-VA2xe-wqDqMD9^De420 zv(3vqi}`T9xgcj8XBxQey+LZa(!}jBXAuw~FMt^Gz;W1d{WZ_k6jXH)%^Y&n(?KEH zTAo0;MK~ZZ8NtQ?(JK}FP`HvPDv^JkC17@->;Uuw0C(h$=S{0_9_X_ToQT`vFNN@R z0zJiK5Y{_+i#EW1K#lT+K7^6a(4A3sstV4Oy-zjjCi7Z2ns!$CNGAgvWxi40wy#=F zw@aEiu17G+p-?^*raktZ$K_mOfPM5Es;m}U>~l*hEN!#lqLUaPw=a${!2G#7A~8dg z0N^;lo2~I#?#oYA(~!?4b=qZ6p+QI?PD?pFtV5nXPMWMeKT&3*ONFAAHECUiEk#Gm zvNy?+OcC;^8;?Akdup-i5mV9E6-n+*G;X_Pz{5I&@`LT^+gHUMd{^sKawLHTOk72f z0EA#;2e9w=(apI42n47G<_GC%CNWJ@0~VTMDQRWSaEvz=2ON;NW<7w~G}CfsnmFkw zo&3)}BO7fh!YD@O_2806KHANG4%1Horj-GVYzhD&cVy)KGy+bcw^2t-&^*5*mNPpN z(tpT4r_}psF8j_zI zsr3Y4ef6g6I>L!pS360N=u}`g3y??CB!0R}xKC`Jxg^Lqmv%-#9mX;F=Rv>{?Q49- z`^Q*4Wzu5|zEn?1v?{j>009*JfYNnc>RKB`R7)-=xW+K5$7o{e+mAo;{{US?yR?uA zysCvFV8KsdTM8Ha^toAIB=E-)sNN$8jy+GNaiv|c!po~}q zp8o*YXhaatSyEZwX?5T4pP)J+MQ)OD@l{K?!iRNkFagIt-L*chf(kcP@|YD;xF;N( z5PsSY!pA0GcoStEGd)8Fh-RK3pb^{U82+apmYitw1vNyJ;aW%$lLUSyR3zi`AP?6| zG>)xU(#a8xqC{C#@IF!$l0C9>qpMd0RMa#8nLv(Nq}%fxxf?7GI;j`Ofp=OQ%!G*BJoKyVn!qJnS+#%p7@PMexQ2k zE|$E~Opw(oH$N0|#|)VnQau39Pt=WUr@vH7DZHbVX`nMCVBmR#XYY&xGw241x17rh za4==JYpZD`N~z|`)fRy=Fk?l^u6~=BzL}{ml*qIG_CVweF^;rrpH$yj+Gg}WO!MFb|bqH^v)01X%3UA zsO64gNg$+4B4=ui(TpdyF`jk0!PHMZ1r2QB%RMZSg(qV2m-! z)VB&?T}KmG2Zt^SI!luIxISms^No;O4fIP@SBpP|(`65EDmj<2*vF7a2=z>-Pv zaZxfz_zz7aEexcC^8Wxo>fc>f6&@1w;*%XR2!DQ=n$;w~#7bJVKNAW%{{S))YCCsU zKj9j(tNMu&^BJj;WR6N@mOgS4Pc#Gf7&srUhFxkRrnS>G68Vx*`BDX$xMoLeDL#ZL zbkC}>*Avl{+zG_1)5HFnwThpHx7;8Uu}AyDh-O6Cz!nj%HjovIfIHxo!QcX~@czc` zkaa9eULVddSJRUhn3)9R~V-P>N<-zuMg}Lv_|1#jwmOZ z6Hd<-8aaaU1gZ`QAn->jKp^)TKVa*aDJaFJMOBE?NdPdMNAo_PYQG*lDGUDJq*gzf|nn|4)~@6L3$3~L|2NYA_B zTq>13eMWxy?eDLa%9yS91&~tz01t6SEDJgk-(JLb(5$E*5Y<_p%xJH=D~M{LC87?6 zm0KGKAa2LFBl?|cyWR{nTWOSti!`wS2=dq!Bzz~(HU#}E3F}*uPV^%`p;9-DFKzoax@RnoSSC5EZCq?0>hdo!~WD8AJE$~M*JTf*C zqDj29ML6$k+3Ra-iTr+gN>^wqY} zQB`56yxXeQaJz-NIE0(yeDkogF4O7Q`fI_G+WV@i{{RFG{s;c1op1XhyC+BbPGRfc z5^w$@^(B8&brpZa)mO2OLK6t6a1~X5l~-!uYN5Z*>u{-%WM+44XV~D6?V_(!QAtsX zkmJi>$0N7;=tQoS)l5S%S)8!rzP^>BRnyh!D8L;|+T6QtZe&R0^Zx)6{d;=ow-oRq zQ!B|d&=AgllpN$JA5J;?>JO7VM+%{iKp6GWs4dbFYS1+EN7MB4Bo2O7tf^&>D-2mN!;&}|`H0WHG$PSrhSgv< z$*8JjB^}yTP|>P}z}^KoBcWP$B}G9LW=|;k44D}7 z>Z8{h&U3opdc)^5v(A_cTsNI4YY-`susF$*4L*tokpdYdk&|i+#l)o=c3jxhoo= z%NRav8x-LoCk(vutWGt4__g7$MMZP4b-zv7X(^OhKX=VFysN=IQ%>Zz21fB7%>Dhw zlYL`vHHG6G8Dpa>Pp_U-^&{UuY0FQA9wpvaqr5G$Q^{nWj^}oYsXvKTY^YU`8Y9Zn z$MYiZAhzNT&^Xb4xBZ~{$EP}~*L|gVD(yW@ZKS@%WxgSrYkJhgBrz6Utc7!rK+A)U zb-Sa#XFiqd2rPYZ)iiaH-KM3I%OsR+6jWQLSBuH5g~1G%ZOI~#%dv2As*A=OHRGVF z?{oNrT_wV{%S{zM9dwWLO-l@WBPWjK0xkz`eRP>D3WXTY_x}LA$Mv^rDxf__8OA>! ziB0uS?O%B6>y782d|ju4_tNchnXQ%7un6nyS$r!(!~Qf#I5D>+dp31n^=D30SZh`~ zlO04A2r4NnDakbT)Xklf!o+;Ye^7D`2-RpvyV>Hv%#_lVddNZV@{|&&9)?e*wKY@?Fc(+A<9Gc30L*sU&Ze(<%c$V| z{D_@AD!JML2iMmc#)jcFC;PL=O*5WUjD`Avsr8ipTZhLD?bv5RDkH-k!;$T#Yi+_I zTVs(W*40Y{$H~GW{z3+DKAOgY@mEO9=^#^%aJV?us_5kST3Wyf5(1@&>`;OIv8WsV zeacAs4INJ4Gaatd>+6SQ|ZqL4r^u)nd@aP$&OC?HMjkJ}6fZgpB%h5*cbwK52m0rjVpY_XFj@|^TeX=Cq5^^-v^WLues#l`f5~G`6OR6&Oo^1 z*I3PMs!qT-^((6G1j*dF#%8*CXwt5hTbAQV9Pu?y+~mR}Uo#m00F!o&_=nT6^w(Wq z>@=~&l(csl(wEC}$Z5-OgsUD%^4U4iAxkS06F}{|(fy)_S1L2aM+ z#g-a-ec3`E_o#8Q3c;UKv_~d5$WzAc3GCRwlgD4%5{%&pF*BzaQS}Y3wfwRmt%1(r zz#G6P^3H!Pev46Dt$i;^ZK0{RR@TiyNN1Ml;XfT~p!*i)kG{@7q;DUZ$nA!_$^}%2wEshKw4Dl0YMV&yWG?eKm*m zjWN_+R~a1V`ba8|mM5vqeQkYysOsC#Qavc9Yn^=ch7d{e%#K@bPbV41g;Q77P#x5D zFBg6k{HN|aAJa^gJs)zMNjJvZcVNmMp_30?X7!AKQJR6`XJ&MU5>LQ+Oj=>x-1L{~P8l|H(kvIXo6F*UWG`uZUTwOe4gU+!OrK(t%l@h#5EQAyI zvAikIvDZb|r(Y&E&&oRv-)(WH?0t2b{{U6hJyBCtEZ@8?v(zY&cV~NiqyGS@QO>%i zmgZrgKwD!wF$<67UfRF_D-rQM0R7^F8tQ5g#3fP~9P&LhB3g-7Rhdu7zz*KI);9U& znt~xwm1fBc^gXkw(^33H0b`73XvffvPIpNFFA;u+sFkFvqIsXpMmtr;2xaay8>xDp z-4(7Yl(OwckyZ1Nw>%u5xzfj6(pFijX1Y>|43Wr_;X9S=!$^0&q)V?(-7U3p6^Kja zl*w0BkY^+8bsnG%w8IK_dxWqqRm&|HhD-@#lx=SOgTecKbysz_iw)6JQ1w(!liO*e zDR7Mj;;FkE@$@;r z?vki0;g%rh{3r1Yk)2g)Hs>BC$y&K-`WQSw>3+4a#ninQb)>J7MOF!K!2ZSs%IZs!wyHjm~AUBT*d3Y zw-{~=@1CRaM$>$XYL1e>QOj_r$WNFsZZZ`8^;uMS*+YEut(SX*w(8LviD_nE;Ry#A z`!}Hs&1C5riO;=EhP;|D-4{KQ=EWE+rehX z0EQon@2jqft@FgIYZ8&yB0PA@*Y#J6gwS;L;wb4NA$*ZB1xW9X-=?Z`YOwN4SyPgG zktTDUG<3H~%hpwqR~S6^nSzybwONO`)!Wj4v(36`nr@+xh$A7^mKZkV^WBeqGTO>? ztFkHkE!x)g51G!;P=P@8&LH=o zF~zZh6#(}jfJxCVm+(`jeg$eS^{`vy^2spBQT)%a?ln{O6hz7F$sbLzpJ?$YewFFU zDv6L*$5_n7VN@wo{(7;l-5VvI&+)KQ7-JtcSb*c(f!G~bmhObH+Juo?<#b?16he0s z=tCdRPRZaN{&JhUR#{U=iRIn^z6x1&`!+R~xWMNiIIxQtl&F48i?La&eHSxzM{rGSm?y zpz=xQSiucV_OQ0nB1X=A^m{mmG)z#Bw|dWtxxP6j?xsvE=$$fS}) zSHV-0&JL}vr|L)|qLcWDHmAQB*F1Htrs>x!RTocAJ|zAU&4mPepHIGu*aT$a1v7() zfz&=CQ&LK4qhQWk{9VqE_-}7cv7DNj%C77YpM6u(_*2#ieVU5*Pb4)-E+k#RgPuVI za7HoCukQ@}JiA({<7H+>0ATu^7T}9tu=68MUElYEh}T=-y`rKfCk_hZIMqwmegs>n z>SUfI+Q5UAU364Uc(*`H4>tqsbcxls%DIEA^u%cz^%f(0S~vSOcAs#_lx;Ug&2MlM z=GuKnaif$p(!>CgFi7L``ss_wo~logHvHbF8l7#j%3L$A9R7N2T=pEiwVHaW9?6BW zf}%uI9|Hq`G{Hu-RQU{tkA4X{Ep4Kvf*rpvZ%u5LC;?&}eTXNvnp-AgjGm-Y@-pT@ zo^juu44!9&j?;{f>8TZUhB*ls+qi?FHB~UlosKen&X%=ucL++GXu+6O+?huHaxtED z5{1c1W^7k!r$I#sSFY zVR-Q1QF%>o>Ih+2(nXJaL^z0ngeP$rz`-~f8mc;vr46e801b7*iL2ryBt$~KNtuH% z5)eRq_s7eht}&~j881|=YPlF?3<|Q$TW3Gw3PJw50D5xO(KR#FQ^@r6tphx;Qxefi z{A^&axrt019m5*X(%#B>JjAtB1{o5nuA;HlqtVG;uv-g=28k)9a~VYe_Kl1ThLzM5p}%C)GdkXb0=rdozaHBzWo zbs7F+_(3Na_Z{`Brh%yKF&2_Vtue(Dw4)D&jq!qb40z|)9BXQj2+8v>?k6M<5ef^n z&!=vb^;X&n8cK&)+iWOs#gs4&xSZ!B>NBT%KUVxjQAz?0JnAJ%3bx#_l1VYEW3XVr z9uI8->WUI-5}nuhl!%*K19rpsK|gG0Kvf2cwjx)}PRrUhU?^TQwTgElgXt(^RkFo)U77KK`T7f;GyU2~pG|x2ks@mKN#qlg8mWAq z?TjDt{Z76^SnzPJe)t@HwFt;wLhN!|xjwnq&OspR;7HgpiGFw?)+(WTZQ)Obnki%` zWZUKyY_L(-9j82M;}ucIDq1QahN9U#PbDj;P|NcY(Gmy)EKh$?>~+bV32~0yb%l$; zB(5b@67w8oqGjX{#80UyK<-3 zRlisD-M^{oX)6*#Pg3ZcYu-!=TYlpV@r;Pb-ad5n9E@@-w6gIlW#h3e-~PCrS<}j1 z%9x=EFqISEEP)s$cyjH|19o^W+O(bbk3I>B|bv5I-2 zj)JZVxrEC+IRVNx+yX%CSe`gOnq?JOVgOvkZZo6qv0LflsHu*6ny-l~Gt#I;60l#H zm7nS}oac{GtSo(1YN<*JN$tXVVkDM=u4exLc%^keGA`@~z+y^!Hym@Mdk0%hX__Uq z-2sxOMv_W7plKkgPy&sGvLbz;7Ene=$UK3f)fVoZ>Y6%=zLK_0M^Ffv?Xk*yq^eW9 z%|v6}lxHi+9{Mn^Z)1tgZh0NcJ+EN0Rive;bPTr~0O%2-=>WslaA&G%`n$0XR^Zz+ACB9ApfA2Dq;8&>Vg&r!aTBmvkjnIswagGQCftJSH3}-&1X-liBYtpf)X{sB`hFzt>{{V+58k5Yc0rn8QchDy;bDs8NjD3Qg3AjsUnu{Z>dag)b5=T*H;`7O-VcIp(aj$FkY zQpvCt+t|doAxG7Ya0UR>OHWzXBn?w>nO0eq<5+2vVU1Al%V7N3I8oezkPe6$V98=7 zrwv%O)Jb)$p{JsOvQg$^9Z>NII-z__fnH88Wqm)NB?jI!kS z!)FKYswKLX?R~hU7d2HIgtX4i6+uXpTAR9Q1dW>eTk%im^6)~l^e zc?PbQp`sC?F?lXfL^Gem>lyF863g4GUs0rej+Wg&9MS(q5)6)s<(N`J>2IA zyb+S#+B-v4e6m~3zJ{gdjzjaNGvvl`l^*PIM1sPCD?Vp;bdv8DJ!K~BX)alA@< z9>A9Sk_piD>iH_JEl(_rf_Od&IKsxwBB1{O)q;OrA<}g$wb!aDD~%E_#ZHvuC?Mpp z#xvW{{q)mpwNTxz6xB4fB3LG_g`BE^f=f6bme5bsk9|`tc@nrjW;af(l2}%TQMza= zB~1SS(}f@(Z6j+$zVokQ#@=`8P!-I?Zg`+Mp= zEta5D)-pm^OB)Uca;&Tm(?cM%o?v>(@FFM%Bt6TK>#wOvl~%|;N`-E4Nn%In8;t(| z%rzo=9Yj>*%DF6mg&(=dAJqQ?YibEp;0B>9?Tg(rv2 zq-^|e@Ed8y{0?wF{Qg>z6}EeYQHh7lh*6UnIocbK$RE>RB94xh31q93LlwR!uE;zn zj~w^z#qq~{A8j;H23*u}9b3YZOpZB@O^SAb>y>sR-)Yf3JW2~PU~y#fL96qh%5;oy z4-9*$C+-j5QK*o|JNa>$S|&fkEyD~TQIX#TkM+@8oysU^WR5wIyp++^ZEW!%L}o%b z#x{@+f!Ez>?RR-z6$-Snh>=b(NnZZ|uNnGhRV#^oz==3rVii+L(w9L@w5qScAaKV& zQ~B%ATx6q;ka?uEajLub8;Nb{gXxI~{Iy(Md_bkP)YUZ=IIek>l#TmhLQimcCm8g` zgHPh6{$0VSE$A7t*hVDto-%mvpnAv;a=#Oo-hai zjb%l3siuSDN6RCH5)J&bF43R9RZsNQQhZIVozd$pG^JHlSVfd14*m1roiW*Z%F}d2 zrD;ricndN4vB2-@M+Z_IAGFRYM`B#`J!ve`M@JDMWv7BUU%nj|7&-LIV@51}Ur|X% z6i*{XO*Lb<@_;P_`!K^kfSsr9omMvLsHLZxDJ1#w6iBU~Fow$G*!I&kmIEpX%wZMe zji$?gHa=Dy=Lb0a{+gZm?ip2^sbeo#y&XZg!yJ+%(4##sa?KkDDTCORFN5w!wxLyZ z4K%4yLXwtMjfDkk9iC!_-5N-H0MW2eeHdWlI$5irsFto-qLx4+jolHOBLI~LkI?D` zd?E;TKl}r<;f{V%dD?qq1KZdQQ*bbcdiM`O)NxnPNlt~Nl_^!ANqK1y$A?_@+zRC5 z`M&yxRn!yBOtjS1!WMTA8q78~3?Di5!7tw)-(4)m-vLy~6pJh|Fp(!=wUf}6Uu^du zePMB;T9;X=WK|h6K{z-d=agaT>ISHDDZIk9_uSMYRs+pIstpqu+8IE^s1Km^Q|rfl z0J_yQ=}=8Vn#fh=hExH3&9BM9?ASZ;_ZrAA#t4C3+oOLlb|)F&A5uBM_S9;MIOBp! z=;kwsp%NI|0!5D>D`WiqK+wo$>Qf}Z_e$|mWq&T6gmV0)nB$Jbf$yB@ zG6>OeEaWlnBlP_L0Mk=pGuO^ojD;B(;~z~^CEF6iaBk(NNqI_&N_vWhV&VcDcV;9N z!1{L3i?!Ia@zX*I?1fQ*$0R4e=5!&drKF;okiPHX{G)d&K?jfOG%#05EnCgB%655p z3!JeYgH6|1LCm``W6Urvt?|;-M9!$9IPy8cDT9x$jCIXgbVp7_VyLEuE~dRfzTp&H zEgdtsWm3JCSpXY=>^S$-yHr-c3N7i>{Z$lookLR}ooKW=ZV|cdJiPIa2*~?n_N{B> zyWcLe-tP4;Raspro@rdI6)|+ar)O@8~tuU$eJe9+ZOZ zx+vC;?8=oJFsCu0P*2!N{q@d}q%hJ6U@FrU=L7J8-#-5UZCF+s<7)8kI*`iGRptde zf*IVeA51Uy(mz#CQ%&hNlN9wGU)EkRI)lN_6eQ}Jn%Y~vV?{M}!KR7Jfs_a3$@-$G z)#cPu(??*Qj`vkbC9<}i;i?ld?P%F>!L#lUuD?7i*IMcdSn5)#V`U&AEL4wd9X0;| z{YuZ%)|sqbOGyV&!6a=S%86MVhVG<+mIFVgylvk|c6`(PrjD2DK(5Or7vi;=+tPh& zed(JW#yZ=5C1WKt=;6VPjHui=BxfI%lW0H{QI2^&{{TXN%SL*(`yW%^s;>~*sV$LI zNto#=Gv&mfU<{m+eMvpew)k^tqP=zJPTFojwJpNwPeUkHDoB~UvB>R$!2VkGm6ocU zzhH9)I2=KUe5oUOIRn!kp#K2vraBrljbo&l)I4L(k#W0w6VE?SLF=LSn@_`rP*|J; zmiP71J4Mjb2(M5WO;t2;fUGmJQIX~_#~@%Ixf<_Rb$f(W-+BTghf`m!G@Ug|;jAVd zmm94eRa7yKM8s@{7GclI9od)Bo9Utyk7umw+T&R)D-gQ7LbE>Db}YnU&-ivI8dT_e zeMRf8dT=wfbdlbv>nHhJEgVc`k%>H$g<^QrntN4yy+vJ2Y^D_5J8tgGct2iFm(#B# zXP-aE{{WFoI8aaL{6{%_O6pFIg74Ek71dWNOC3VWtxpW(DUFXXq5MHk03#zjjdZn= z*IjGq%UvbFER~-N4NYV!r9^VhyPP7Al!2UM91wNO{txt->pv7NHn*pFXycKq>s3b& z3;;P&eK#;w{Po)xS{Wx;AInID$Tp%JsL9D3{W3kqm({S{wFW+6sz7=V@or@!F07NN=zoZx zmk<8{qgIE3R`#X1T~HikeynH@^* zAFhx`D%-Qy_?voaaibaIeGItFpqE-oq>~Z|K zg0hiatQW5z$CKhXBj!hM?c$8+D)9UbKjk8D$;z2dz|;RxVRn!mNG5CeF5`<5%Q`A*g{_3`6q^xwNh>_jh!mryUhM&B7>5ukj z#r~|bPU@e#uD=NyY#lUE&A?G@Fv17iVQvC8%?utvH2jevdgq$_ROpgtG-pZ!eM;;`Q*{7N~Xj(SO; z0}P5^w*`ilYG4hQj1buO(>@tph{#-g-0(*|v<9hFCy@}6ppZV<*170Q>DYpC1jt?Z ze@{c}t81(}mY=J_T1$WL8wC}AOkC!PK%Su?s1i|`{aE=Dl~y_VPz>s?o}F8m z`}%@=pQf3*4lmwu+;lCivw@@*R6|j{NEw$3)@*tvg0yO>8n#RLKOutcciPeKj8^IMn;IZ>xCQ8>9IN z`uge!iOYpVELh+GJLr+bIZ#ZSNcB<1^%U*$#8-IF9OMj-Y>xi`Z83EHG3twr9h%u5 z%KFI`2%+?m$>SQE(LFN_L|0mRs+f$`R=Lq4br?xhtBf4-cB^ClYz-vKB-FJ8DQM+| z{{RxPR#sEo5y&8pG`L}5Ax|PNmAQ(k>Rz<{2Ny;n*4^pgsgZIV$+;9@oRwk0!32I< zT~86(+jNXkQ%v?vpAT7XmQR+lnB`tJAPgO>cD4o>1EJS%8ajuqI(Cz%I;h7jbQ1`y13Qhc<=@c}TW51Zo ze&~7acetUp%+=PK>UFqU>y%_vQIo?Qc6Gr<9*n0tX}SLC>N=W>opm&A2uAXds{<1p zWwNAgI2<3QG{?}E6}k9ldFk4TQ{%dIW$Fq!q>!q|OwO^^LmLiA`Jy!P@@-Z<$QkR}0t71}ZG|4=gg( z$|67tG?DINeN=7q_9q$*QqXzK&rht6Fx8+_Gh+~pN?0Hm%LW7=>#er>IWIK}M?g}dSKuiF{QKwxwDLgv zW`(0T&m&(_Ng+I}Xb}&`y2ek>?FQ@=}gwg?k3o*~@qx2W(?iAFNwaf_* zG@;v!Ki3_>?eD4}ard0Tfogr+&(W{g-k0}hhqRJ3XW}YE1*A@JOls@^_xYFdI_pzq zN2ZFF5uYwFGDqwBlde|%jFzDEI@8pGegr1z0o)k6D+b4*DgYl%c1;X8p^Ux>Xu<8s zI>AAfZ2Xz&m54?CB6arQ)|!Z8QI&|UO_v z_k)4YsKny*> z#=k>rdMI9Pn^`=$V?wy^hX>!->a*(K59sa`wKO*hEz00AhmIV7FgjgrwwBLgIs3cD z)}Nm}FX7Kt-Dj_-UpAVeg_<9j6*)b&p2U4ULDUh^_X@d%zFIk{Zgk9GSoRPD=g@j{ zuKDSnh_a-YdU&aw1#DmzE6O^I1JAD*(E3jWsI9$FFIQ%HlA!=q+5mr`BxD}kXGI@T zw368UAP$O$E&dQ%I&~qoSVgc1h#T0P=lqZBqMy00v`s2mtK+PyEsgO> z{{RU80P59)d%9UA5Pt&5#@t@G22Mjo}QSAF40oc#3mtp$){uh`VaSH!q67LP3Qpj0B1Tq(Oomp zcY9br8&~nnnFg2w$bG%ew^ze^=+|nwZS=PqF!)M_T(;cyJ9*B6jFl7Vk9X!3t80MY z)LMhV%e54Lyeq7=Zy&1A&bzbvoNBh~J5^O3W5rDqY#)Vd5N`wO%l+}IU#fbVn&)Jc z!B!%uXqlEcONk>GCrcU*NW>C8gB4JFbuP>~_h6Rn|*2Jv1ZdkPMUb1JvkVqBtbX z1Ep2vCuVg20Ekv9tU@laGYo$>eK^|hl&w0;_>qr%WNVge-8In{i*ys%JWQv8<1PTE zu8hNj{{T_bvr%}f;k_!{QsGT?wrv@no>i3;eRIgxdm1xt8m|JH|^rY9-rMhx5NIvrN2`~@XjT60N`oP z=hZOLQ-LJgS+X#F^+Wh>zwFJT08p%<12VAn`s-&<^(}R6P?AZAMJE7pljs1|(&V*{ zI-ZWCc0f;ZFuD{{T0Y06Pif{WVW?eHHTm09KDvBMN@Nb_bnw zFG|^=pn(FY&NX6+#vdl3&v3n}*tArm<-zqjLGdT6AhOev(*S;MHD+C^ptsxFNl07{ zb;&+1b%iZ`#z3;Oq-B&0=O;_v;XR^TPK|RGF%!fu7w#2%?UuGB-AFkY9G`tl@X|~D zEJonZ8LbJY!28Q`404!p^ZSMl{)LOvSoFRAX3OAc9ESanGl|hH4U?pD=ii?3N_T~$N ztDR&1D>Qs?3gn%AH~A_7m00b-JdWCtQE8);kkG1(dJSPM6(uz}L;*m@ZA&)Mm^_9! z5Hoc`Z^IG89Q$WlYVXv=LoXhr_tO6WP&z>-$hakrPK3R3Q&R&hnIL~(wyvxhV(*kN zz25mwVPdj?!v`PhrJ1krO%p`y6z3WRbG$(#I>cD^&wT`{xT>xLuxl+}nqrXizV0dD8qfui>N zxe)vmb0kW?!wMJi4^yF<#9?mn^BQO?*gD86l5MAU;N8xN^))p$-qj^-BRG~6ZOS#E8No6%P^l1|a6KEv_@PF9YniKj%#9~7S{?~#u9`{-p#VT2&DY#1>L^-d|_ zGR)#xLaL}7mvVSi9>YK1L+85Ed?huyxbNFgTubYT#rrbLvkK)@U@9+)`D?VoK(s*&kx)}^BW zG8q8`6(r!1pI+x&>AVpkP9;w0RPAo#Adg-(pKK+VJ11;-!E~`9@>^_8nTrrWy3T?v<>RH?w@1*^&MsMj#UGCWrndK zxRCJd-Mvn_cXB>Yujj8bk^$pAj)_%U-m(>|q(Ed?TT=`&(McmkhhLX&+z`Ku7|*YNW1{vOTU~B(MC=|lVUTateB-X;N6eAzL|k zm?%|c&!O6KJ#*=#HAzxLh(2S;WpyWO4}bf5X_b17+@ur4hA5R|gs8z(QH2D0@ujI} z5KAmrJ4+qPMlhck^_Z$^ zN2Osy9I+T(>GzK<^T0WC_)ZF*2+16d)m$|W80}-_fs?p;eMXE~?(`JOVUjmRnC=b$ z!tMTGJw|xPwshTgp0h1&S>y?4)pccL_ekFK4RtKPhTxwofO#n_X92KD7&tr(9B7>d z);p!iMV8|gbX64Q3D}dk1dWIB6ye!(fs#1L!ESX{TsnfS1Sm_#G+B+950~2tImSlZ z>J1g*E#e6wDw4P?!S=9W+YQFj3CJCfV~tlSR z$ClpeLHSO77-t#@1;=n`>S&{>8@^^oFw6+!k~k;oK^prnRb3&dSGYu#WtVFTqS=sk zIFMn+M}B>@BIbg+Q&CG$3dl)Rn;XCBE-{uLP&1&p@*x!uO()NE>G%uFNiFJ@c5Zyg z;;2$St(f!r9c+f-Y^Yd|$9&m5ngY99-wNb<_Rg18tDsV3WRY3}pYM{K`bE-@MDNsWM< z5LFK4`B$+1U(ZUhvP$4pPn8{vxp2Rc(-l3A7@-r@vGSx+0hRH*6VBBfp4zflM5Uor zd!&oK-Z^foQypweP|*aD6(z*`!yZ6f6Z2#0F{V2wR3(_kmsrhhK{zykZc1=K-0~C1 zBmhE#$FH`Q>MAW1aRBf@55LCLG(btPDV>M$FgPF_;YI-M&(}eJd&NxKA%O&c{jGS; zdxCk-=b{Trm?2-NI16ZcjIy0 z&mUh;%Uo~gDj>Gt?L5m8j{O4Fytjg|)YHK41o51!vmLSLcYa$q^xApjMfz5%rE6%Z zzAt4X*1$}9G|9``g^QwmAC2b=_2hB`G`A!Rq(^~BlY@fiK)5NAoy^}EHIGU<2h1z$v8X? z*w+4@zART6u8y)t5_wSUjAfh_CHeKv0LT2=?^QwCV9Jm)IySIc>u(K0CJVI}Vcnl{ zNFB3+Pvxa5`uL!F6pcXmnKF>6-TXBqsQ&=ez!pBo9-6D|y>)PE>F)KlnW?A}ZjE0D z8E}~P{70Xrva0I~h0CZQt(BBTLlY{h;1wb>$9$f1`5jv-24rk+hv* zwY{@Iv^(=O<))dt6;|xO`RG2nRHExiU@5&@aPx56yKeRTj<#0yZ6(Asb+n@)&@#ud|M=>xyqo01-%^6dJ zE(Rv13;2lRSbiUwc)-@iR|g~J&b5-fj1b;~M9;(^>O1sTTzoA=Hrvd(iRM7F<0N(% zZ>FugXHh`{B%WqoCNbcg?gNzsf9IyTra}&IdF{rZtT*ZBw8Ug5auenr*&T`d=V{Y5 zzVWokAG_pB?DaJ>s?HCU8lhHQxyrf%e!ckov#8ZK+B(~G+Xlv`Vj@)>7}PFDSpA?WnVLYfw>BsCO+KrGlv3T$RW6z&gIrIq@vk zgWP+$T`3}x6HpfwFySO$BndNxjv>*|Uqtx-nlCHP(xo&#v zzr*bWM@bYfBj64T9u$6O*S3JmcZmiTmPS6Aln>|zm$K{}D1yVci1Ysd*AnouER@vo z3>=R)<{9_n81z4V4wtGg^O)P?)HFN1va9$}j!FaEu6+-=(viMY039v#;~u2B&;I~@ zt%-WAmNO$!(yy`F+~ez=0JW{=RM!2;)lt+`ZZb9lHtYe%*lGvvHfAjpqjO}IBRT&7 zO=TVGXCust1^SWc`s*Wqf&!*Ul0|L{#CadrR3hd*$pX>9kSZu^X@(j+k9FrnEl(5> zypF+Hl`H$LXd8p^qj@GM)*Mc9^NkTiq>4qSm6A^^Grl-n zc2Vwg-%ZrN>^f6JP-*Tl#8{$BRD{JL9l&x%PBF$kdD9n8-8vDgmN%t|Bm6+gvn7YW z86)%1+fphxRmqB3sR(&lR9&RG)WX7=j-jK<#*UFT(dtJ7{{S!fPxfjrM5v2DBtZtd>J@2^cIMLaL@Q#;mGeA4dV#+}0B-MA;$82aiP zK6~&%t+tK8!4()1K}8cKiVH0?1HmmdCD+&zH$>3YXBcJ8LL+~;HwQQT=N zK68cIlcN`7?SOOi8VlMiSFJl7AL1n~VhH$=dWG1_JY-{Rj=1fQnCnkV(%NbsqDuRc z(8~)cS!Ma6DBLo#4oE)ZwsnV6OBlJ_qK-i)#0B+QS`4u+B#98WqVZwaLnO3X^8VA zInRCUMyn6nE5(RBAn4ie6m*gc<$FC1rjhyc>To}Z!_^aV=iQ2rU1ruA)+Bajh^*6C zS<+q(ELGh*J<^fVXl1!gO~D)pNJa#YW1T(u5ydmJ5{NRqa-bjb(g*D6(iOfRcu~_` zA6q1nPinTrm9opsl2=Qas$I{kd6E@BRXSiE4J5S^K1ax?rLuGO z{<_RtQ2U3IHI;oTz&-^0iT$PKr#?OP(WHfZqkX9ZV9JxboP9H-7_Gu}{x;NB=W36e z^B$Tz)_0}8buGsApE64NnQB-8k~g_;Prigfy^oqvhE6lI_SdGQf;}=_?X0Qcc?NcXpl6;no7U68#F7ol zGD7ln@KpmbTP(7}!J6c=Qj{c8#ubLs$8q{-MPE!=Sm08WCxPX$UVpZy&wM*^6h{L9 zZfu{^QO$3~9Lrc@2fxTpKG^>NJyESy5Vf0^oZ)57xYAfx!dYr!K%pVeg@<(B`u|I8Ra;>+}#j$0cqM^a^!N3{!KTR!&i^yi3 zjMA~)&td+$u6;xx`I?%0Y+;w{^qr~uBid?dKWSd1zEOor4wj>>N!WfJ&pC|%^vKxF z_8RBjyXtBBpQ~=STdNsnxYtWmWs^C}J2Cr%oPJujzh_?)?H(L>Q`R!wMM`xCP@~OB zQ6yWVlo*;A!();2Jdctwz|}o@hxd&l%uI1K2(c7q1h`X@SLyS2)^F3Odo^~u4h8`H z@yuH1Oe!hsIsIbm6BK43p?M{P_aoa&^+$Bhil>J-`)P)vmf)0vzF5@LGZ+8WYymg?-g3vdt1MR5mcn@3;_s@-d+C zc!7Vh^n|@NZJ5TDOzI~Le8^FN-l^@o9rMOcJL#v$dSE#a!bzDm0C{k&e z7&bcs!@s#2t2)AsW4TBvAQ2>=#Sw$TXSg3s>wsG`k52K*o!Q@ZWzGy(^b00 z^mOsNr}5`N&;I}-o4;rb*!Bq*rKw%3B#1L13Ahk&LC&Fgh0;VvFHzDXZ&m`aF~_%c82Gt*p0S9V{I?)E24{YrfH3Y_$;;K2<|cPC~1O zI8|b}!aPIiO8Hx3L3yO} zy#NM4r19u;qrN=a*2&`cR$F6@f<fn1_*^ z>|#OJha`++UkeQ2F+BTgWjak$P?XS9&n(g%sT^^ac0WY`ldWUdFj$y{lL2#;7$Hw1 z`sy7;x*CScA0f|ijG_MkOt2GT zdzHzp_0&-96VF34H<6qHy&YB5WcR@zT`l`n8@2}-lCI0hm0d-mwvrJ%h^~xrnA>n) zzqYngD}f#qu~cRN4C6oNsp4OUW42`roG?9(r_g@yn8Qg!P9UCbkOz#DCgyGkCpaHX zFdya|SbyC=Nk3x>O6pGy?INmu?^v;!1B^0~xmEgrfPHz_Ytyk5$`IfM3O#rn4RR;! zdvB&T3w55};%F`E#pG5b6Y{9fBm;%V>#n&;k|hxl^*>EuSJ^+4Jr+Hq9|CKsri@3t z=PFNq2$IL<#WZmOrbFgreKDOf$brx=eCHa*2ndjTyknnzSf*zS2oWKvC$`m18iE6S z;QQ)*hM}HmBXAhW-`|ZfS6kw!k8>!??AI47S&?l{#!d+Ug4Yilj_Z7dSR%f6s?$rudid#UV5_3xn+R2;?z>kdRG)^$~I zQ6fVXOsyM~S?Qb?kJy}lo~nw9o|k&5C+c3Yi{cm&BTG;4((&~NYkGZjqt#E=-9c9% zvGomo83sd1S473)Y3YIjUG92?Ws0J?L#LVt{5Qzm89w>O zfomf4V^yPLsLgM$=xVvPS~}JjX%~?s0bDOkj;3mE1FWJV$MPU& zjaTr~*zR}9ZPWB@mK(e){3)sG2#J_~EQ#5d>NN)O(G^#zrnc4j)Yk;bjyaC_q~o@I zy^gDj_hg=0sQM+( z+3E`p{B~^<)vz|&_*f>b7!Cc(F2AQ}ED^^oMVjYqt&LBWE1V>a>9vOd{(4P4cRa0A zOL2O%m1M)o5@JHW<}Va7@b6LtP57p~LFCHZ?0{o|DvrOs8nS){t>=#if;PA2ykk{l?;|-6 zkWZo2o7Z%#Yvfd|j-Zf8ayB!5XV+ELOe-9Oupp1uUW%0qSOR%HLzc9b?nOXhb(1}g zajlP0E97n^bDe9GKg*Iny4_Di(w*|p95MQqSFe3F>Pl%-*XeTvFmvjNF0s&aCeTJS({LuT2{2WOy#sOT2>y zNMAv(b)`R1Sm`RI>Q{7?*KthxeRbykqkAodYwM_SNUa5or!jUuzNNKRJauz#$;RS; zuAMp}ii@l6B7ZcrTe1*yoa>QqJxe9Z{{TG5Leq&bfFD8Y_tm-JpNy0pGglon@~dxu z#NG7GS|+Zo=h6k$dH7}J%Li2T;)8m)Resn@O~jiHr% zzA?1w;H0(3M?^5$`F(XkYbfS!vMJ=me-LD?xze>2GnEW*O84!mDx<<*ij=JG-#Zd> z&bnnfdRvuC6jD^4Gq0CEgi30Eij~Uk$^)(Ti;98<>~oQNYnT;fhowC^_ym9ype4=a`7r_@WpH1`eX788Y#V4IjPH zNT9Ss89EOzW2pCwo389IA29<-H#@OFZQ%x?8R4cbvL!$NsG=T`w%TZQ$g3j^6qKQF?QotTIT!(LE4E zRNu@ua=OMOca}hTB#qs)yH|Rql}f1IaC6TZ6>+MOl<#clM0G|cC5QCaEkaSQ&vISf zrZ^$qg+}afIM4~Dl4yw!!S>NQ*(v8B$Tx61>Kjs-LE3wr9R>tlvd1%TPfk=7Ah$np zrZ^lpLBu4IN%j2o0@Y7VO9C-v4bI`7`ukIMr=*ceN0|sZ8x6;S4O{FS#(GAAoVbSX7u&s_`HUkXc9^*x5`hqG- zW@ywPWh;+!>VBHLQI(WUPS)mZ@h0Faud&of0IpLAYP)0% zo+BCK^Y8iUKT_TQ0D07jl0paJVfN4|DvXX*P8WfXpw^`W(yTAU&ba#47x|2jdU}hD z)u$xL#!toAcmDuQM@qG7)N$EKY-hJW_s~&Nc4Rg;3^RKHt$!KIGVn-0ri;8mgwIi? z8=4srj$LLg8?St+AKP1Jyrfj@XO%>65nMkYup4+Dg# zMOh%+wDJM&MhErN1#MWQMW>~LD7JubvEvL!*S><(%~v_yB#Z&)md3lO%H_J7X_|UN zx}r#XV1n7m_9OoQ4vD~RwS+Vkk<(5Pu!@?Q=9eR}&mW-D+@4y2-I#YH*YnouHj-Hm zBr%Pr=2L^mrhDtZGUakJn9Vq~Qpg42t{KQI8Xb=7>&D^+e!3G~aiy)PmZ2#e861!v zZ0%mbMm>N7^3Zk$Ha*BE1n0h_LlV9=xnFGasN0EONG~QDwBIGPtcH>Z72R>XsU&@l zcp|zn5C*xSIQl~#j22C@Sx;(J@C2tqnI)6 zwZ2pShxOD->Um6Mnl}ub@;Sz)d_78&ns?8(H6k%^F_=VE9$RN11N{%CxHb&1u&K^s z^ly+*k`uce=#8Qjs;f0DWAf>uX&oQ7&@qp$bLp()v&RfxV;KZk$VTAj+@D=#N##c! zKne%QpkRBj&-BrYjByOsf)3fPNn`_VPT(=~au|CZXfh*=ACUWZ8UEv2CFGNhY7yrJ zfxtSdx6FkiVkM|mF_$d~IoQW5{{U@b6+p~xaDSBl0G77-WF9?r_2cKZt-r*!;o@aa zB*y`e70y2TDN5Ak{H{yjfE4yWQ=m$rKvS=b1|edWNE0Z-&xWAV{gDQ^9Pv8T-?{)j>qKfxHGy;W$Y>G%i#N~;=Es%4bIL~3G z6rmF_k*YB+=GAiCW~!_^q?OuIK;WG5$FS}88X-pV!DK4|V>Miv#&~LYfyny&?0@>5 z6T8Dt5(W{*-eiR$Lcq8uw{GC*EzYNHYE#pyD$qu>=)*rVFXL~`iS6yGeUZg~Q~?Q6 zP}EjatQ62OWm0^Q!zw-0p5xqi`{<2rzOsVKl<>stXOzt=s+{Zq1YD0yf=}1&pmeuN zx_|eGK#uzPH~WUz$9uwR?j#w zYZkzA24ah>t2|`^U8Hf0Fa!Sp4v0rL#TtM?b^(AaJMK8-`}fnO1?JG#6HF?jim7(y zmak~t>$nmMu=XI}gQ7QXma$)DbuqmKSo!zU#y~C4EC&a_rmPXb&vJ)TjvQiF(9=x{ z$t+Sx4&A&lAB+Ck*4RYRe9{b$UN*7MxyBFX4!2*d)ixQGZUKa0vMM5cBa@6TBipup z^g5>MvBXlHPVtY#xZ_>8L#uBY6MUBHN|qufRbj~p0XxzghF4jCa z{6rtnewvUH7u2K8c3+1qMn(z zQOizejgz{o2M2R{?&Ab!M6T7;)fWE%BGE|=BvHVSMH~(yi_Mk(c39O^ZYp+`JBU$% zjOb$RnNbuPgm5=^D{H!1DegB4OLb$^)5TEfPcg$Z>KK710AMyZ07n4hLZ*!|%7EVE zjO#kusGi|jR;w46Eb=-KxKIlc2w%5hs4~hN;viIG9GsGW^V3teoS1M9X1Uon;Un9g z0x8^ctml@tjy9y87J!foE3O#(cjRkI)wL9&N_Q(VatilT_xIFkrjn*co}xyQM>%F< zNhj(~jH)5kGv8C=NX&jw-?=AQE*k;2f9X2F4sq&rKAx148JE7fh*6U+%n(dgk9ub; zN7tPh>FMjOb9vO2GAv`fS;{xQMzli&jH-yaX5mIVl6cb_(4;5esMI?ufJqnt4(v~- zInvJLh=teWZ9Zh_CAZ$4M3SW?d!Ln<6BGLeZ%%s?`sgL-{_UfGi-tvD`y~gG8;x#R zVyB8k7;~IH!^t`BPNH?4(dW7EtfxLpByUL`GOq|wsV0A$AZI@M>ZxMDo&>EFmO1ep zJUj7Dv!$pcwY>1wTB;w+_)3a6>flhTdF#nr}~zref}0vMx*gh)6+c+ zkmDS>w)%HJ@`MNeVXBzks-bjhcwt?KB)Vh~pTqPY)auBvbp#zrNULMTC1osorne8m z;eVPj>OZLU(VtC8JC%9)hPe$(xD5XQh-wR8#1m7|JxL|iVqD2i0j9Y?4}8K&^9R;8 zV05#3j*hl*a2DuNswKqgskN#mUJFUXyvDLeVRvnI` z*7f9f8pfsSn40R5sDxbO3YEZ)Hx?KZ3~{yjIN^ahMb*&dPJg^X0nhHo>k{fr{%grn zAIyBS7Vb$s^*+aIT9Rw3llT!IlpfCB$LXe)uu@jjEnHKj#?>nrjtuNC*`J`T7EcHI zYp<@>|OEKu)p{$g=bTUOM$pu9>tWrkm56gf-U`DJj z8m+cp5xQ=l$NrI|NTI2bE&W$b3*=j_Wk-`f-r^~M#_rpI2cG%qU7AfvXX*U@dmr40 z!>F|asOW$8Q}q;Z*1{uBFUOG`$snwsmAMz0>ziP^*TDs!!oOzstL5&l*eV8+}0V&huAUy8Cf03ZZ4EUh>bglA)UE(@9=wD1)!A1^V(N=^Evm9AO4?SD zW{zn801|FHzv3v|yMVzSppmRazf+^JLm)r*#N4n`^%Wr4N9b|<{{Rz{FP<#)+erk+ zr=${7(#K978fdA|h3rTJl6mJsA?r9Vl?fe>qUM`qfy(*8vc9>>;A^1zi}p(BYn4?+ z(#h8qoi6ej9nwlOTSgZjg_DLH@sM>v-l*51?lbh2i>Nw#swRX7iRdMg66=%l?3odZ zgXl6o`qNWI>AIymzd4?NS@hdz_c6|V=0vS-p1f5}X1#P;Qq&mPm&%+L1ol55@2bxG z(X&xUB$O=Dq-x9-P9FgF$Ep7SO;4+@zuWH5aF&{`hNWERbVYIW2eHwbTet5e9~oXE zmQ*dzD*5M;+;gUDOSIh9ay*XWz}ZhDsa3quK&)htZcoIZWd52NHR+!zM*vQFcjq5r z+fQ9*(`hN1s(`Nf6f+{OH*wDzLWY7uKnPR=>#ce$M_tojOlz@sNXm>`dlqJ6^D}Z- z>7S!IKJV2xn638Iksll6l~?{0W4oVkVV!BVSmmBX0d|6a8xK#Q*Fp3&R(^x<-s{$N z0tBI@sE(3S(u9_xqEurT#&<9qN#lX5J-{4!GS*%G)$UhbBVA#pj8LHRz$qFbJcNxv z1O0T9WU8&MtXe2zGQ>k=2+q)ZnT6%bsEb0oJd*_XO^428*pDq3o*?#9k0|SUohcd?SM)_SwbiZ`HJi%#tlGo)_ zr())sQ8ZZl9p@gp3)l4GlHn4rIY!U8Amax_JRP&tE$zC-CX>nDW;Qj^IR%sm}CMQYA6`=;Cl0= z^ES{_ZUp*$wT!3LvD@<$xR$WXXTi>zO0J)|^%^o4rl^jY;|DH51ct}3JZIlc_eia@ z_K8}m7$TN61(Y0ljK{y!;AtnqE~ZYJvsEL~tw+Y3`Ak0-r}XclJw$73Vrtsb$}yFT zw!x(>)0=MhaWeG|H+z4Kqr6|BhD+Qx7@{F6Ap>?eB?$+gu^O(Pgp-KZ*Ra*e;^cX> zTIP}uG+E1T}k0TiJmxA(^_u#JH$fMakX4v1xyhvL4yXz&gM}d$2mnK+gwp1i4o8Y zmPQBj2VbSOzMi$*e`hOY_o>QSX)2Q4dwHr{G&PVcC|7s`xMUm=lg6-r{RQt!Qph0kYIhl6_s&@?m6Q+ z=#LG0XQaAY$)Bevprf89QReR=pz(r36S(AlTp5C>C1OjQ^Q+uWWqFTQlJ69%2pl8BR`gw zI&Z{osG^`V-OI}qhs{G65rM`y$9+S)_=Qh$Wva8)#I;uGBAz&EcvfU=fWzG52LlK5 z);+G?ukIIt<`CL!9_c{!4^CaWcHk_Z$l62Qq z3}sgUl>-5EyLqt5P>UPL!9(sFc+vzFUMHig9Y)wI(UDs@IaTl5-&5mdCw_YlG@nQ3 zq+ot6qjO+mliN~W_Ryx%2+wTy)z-)-tKu_C72+xm4{`L=32DKcGLQo=I{3FJzF-Ie zR0H=qkdGXZ0zC*B`|9L|lmMg=RZx`7$DZSzdP=54xD4*@KDx@f1!{$eW02V%hf$ea z#3Kv?FF6BL#v%KQJyUI+;F_+PizJ0ujOTAX#;SV0n~JjMJKQ2>-8gkDGP_8@A58w5 zy6ShWT-1z(VwN&?_vh)V{`CaZ*CLectrb8nv5~`LgM;XFE0b`{H7~T{P_~Z^ZVKuH5NNX)48#sqi03%xlwf`HRjb>6315$ zk`;zADD-c*<4iQSYAcCIYAM#1WF{J_IR;ev5Iu*e^zEW8L3@>J2|Pqjz3518@xyVj zQ^OrZibEZol0T5J{6pBD+7)N7z4Yyst@j8_o>2u)g@Vv>rVC~!<_ zGr=8(tIMBNtP3%_QmY6Qq{quF3jY9%V~{<^rj3pzRF>jboplAO_gN%V^%B}Hhy=etsLr`p#c%e8!BpsOQdH1MBN(M#Hk|UUk5R6|>T4ydsHoztv{t+|ai^P1*;fp` z_UEx5L#k5!(Uy+2nxfNimF>6YZPCdZ@1?02Kk*|xgZ}_B^Y_!5O9gGY`hwP1>sPTm zoSjv4shFRNo6J_vB$J@i7?`%kVsVfd_S3gqbS+m+R>@6OP6pN?jWZb#5PN})jJ z=Ego=+B-EeML|yyz%BC-G$P$j)6~Zoa?Oo0Tq$Ju?}Sy!ja7IW9tm+WuWS$}+f6W% zWsDx=dTX-(0A=3+t+$!t>U-3C(i}WdpH}xe=ARAvGP3)6rlXE5hG1Ng*n0D>w7huD zN8pD^B_t8;m`CM98k`qwcRsk)wKwfiN_JxUQ(vv>+xCy|EH4=RQ*RFb8qI20Qc2Dt zB>{JCzyYpu@$atgH2pmy-7DH_jdCbrPvFm>{IoB^`^ERh4-lm54y>M9rKD3Tp*dK` zqK`qUyY`Ffl)3dya#Fc4KtxIHpHr*@r!T4%w~kC^+n-Q>1mF+SR_k$+Rj8F;fY==C zO`hpA6^hF_OnRTbhtSAHwqj2uz4aO;b$>MEDD~H(tJ=|pOcEz`e#)LG)6yWJtFA93 zvSD{0=r!E7`;YG$2-zebI3Ak(a_~~?UOL*`DWs4nokr2>Gq2WP4yxvo*Ggpsw!xg7 z<4Aow zX=S>{z0SzXHZ!cpA#k%sq}+E#YXU;is}qxtzK*PsD{Tv^^Q8KcIhkq09zME%hD3D? zPk((?s@n)(nG)+5l`sdcm!)*12YJVRHP%Z4CQgw$hN3nlL*QWNNmzCq!`Ft8Znp@e zMhf1Z`mZ{I;-#HSjt-~aE6p5E=3;oxs}7~=Ks>@6?&F;3b-wn*D^>u^jnr2m3-IHX zJbGykzM*PH;`jg)#_15N{PBAd`YyrrhZ+8fA z(C0W9A6-MY)1-&YmA+Hyp^oNRG6`-6uBM`vjO=o}4C%wb#LnH5nVrx<3Y-yvk&~r4 zUI(bpnp@M25xT8QsRt}GkaV@@OB95#Ao4gmt!w61$1#pLVh9CK<)|j(>~I9iU(!=A)2a}GR6>N3CYHF675{nbPRV5&xKr{PIVHx z<|CDhla9kdEAA3jNWP%~;Ga{gZ3M%oyP1>JIgG0umccpq))AJ$FTg+f&as6!`N<^r z`s-+6{HxgaBSbAYkUE%|8c&r;+ux0N?!m|)0pA+Wa~u}eiOv)Zap|g;k-BY)Gc#jw zL{I@^*+z)NT_rSrO{qmFWAjupgFF4d!=b{W7>SQ7$9#CncMnexB#)tyG&UCCJ_7Cob6I>^`SN9z_9g zHmZ&kb^}*D2qz_Y^CQ&AH#tB?Pb|kQe&<8w?s&&K-%&6uaC5Dc)J#B90LxRIwJBz#o3+?V;6I zM*)C~1$z>I%R(iZT8TW#WGvq2(^9MC%U7o@_jL?SP-~aXjq*0&1L$?3A(0igk@H}2 z?W`>WagehRqaM6y?G?B8jZ9Rr_mQwq)9I*gC9Gjd$A}xtY~YP<5UEjt^gm5Fo`$3| zh$gv7PC3lXyOmDdfxzb{kG_>_Ef;%xqExFWl}pA-jz)3)!PPV32`@`{QzAS;2J%4l zI?*J_He-pvBlY~Xgy|>EEToV=RL`j$^%r6H(G>?~Hi_plWN`@3Zco0qpyROpH3cM; zV-DB{w{fWvR7;F9t7F`MrmR*&DU;%5&Pn_6rrwpgM^eV$Q6U>fVkGnS(n3!WW4bmN z?}7-{*(>9s9&}PTlzt2h9O{G<#9GQtoIx?SnNu_4hGsbS%WF7kSdnGalckA6}FwFlY++|Dh%T+eFnJT44J~TQIj{( z-6w(Cyg^PHANYBXAi3?_`f`6EsPWHJC{>D?ADm%h&aMub@Lq~)b(W^3Yh;Tg?i*>t z=jPl2+<4$%XvCc#PiLwTx>M05PcTG@=8%K45=Y9cGx_5>UG*a#(U_ZRYsm^TDG^aJ zPFh=j*wZKF+&)qF&b12aS&3;3s`<&_9!{>K)3i5a__}Ii5oQg(I76o4{Eg@{#(llC zmDXJ(<8+YPqgI5OB``2%*^)Db?xShTWACFQ7G1aBsbxERmZoh|i0Nl9ZdO9+xm8ICFBesSxwcc}xnxYWyaiq=nwzlS3k z7=}EN^5gsKLdQo=i&=4m$r}P-h(6YsgN(PbXVb3Fce z94H96{{RR85ACSwuw`j~QyGvYk*0QHsxnF1tAIYkOjZO`qlt=!5>zuVKZS~b06$K2 zw0@deh)l=mgYR^Fu z4UicKI~jguRv_{ZIr)xuVu-K})dMk}uB)(K}Br;+zE=3bC9;EGM1ok=5nt&g< zlR)(t3`)qLAeTRf*Bk+;Q`AU!gb)G7;xJF!+gL?ZWvoLl5tFxWduO(YOG%aFfsb>I zSo=Gg{{ZQpV6|4yZ%t)@cpdacga{{7Ei6?SKk($v(mx^&q+M+=z#^6j*KCL8iwAs1 z)ExI7$5#(0)*H5%DPbjos+J_+0m(WuZBINP1QU_Rwu#&A)&bM4JHb}>IjVxwzCy;d z;ST0LyZ83T7zf`*ERe@*m?bSVMBsn|XD1&208Iw#0rrTtrsV?zxD9Hk?enWjrKCmQ z6EWI>y>fe;dH^+y-9=YX5r&pB@&5oYk1B-!0L**0>-N!3sG&m&nPOJmCgx%WNj&6e z??uw0!1P8qnx1yd#G<%fFbL0X`PCNnsdJ8H69qS_>?(}H*xE)w6j&wRYg}tJdo5v!QzrKLn{w# zYw9lc?UUGj^ucMNijbF^8}{Z&0p$nyRQsP_)8A6>>5Rv$upq~AhP$b%UdaR2Q^gg^ zsaQ(s#z!HW7~qk|-#G215*giNMFrUQ(Hok{P!_nW10!sXGsXbnLXTiIGTm>2iXkk^ z=G3vv3}sb)N7U(jlkRAM+fwcjmrd5(I%zlCO9zw#IAsPoy_Lb>u5{UOvks`~x9VGM zx=5i(gmq8@y<%*0EM#(H1p4wlee?p$K^4`5)aFTLb%ve{;7G*r$EfxGy68TirndC0 zwiPGTSv}@{6p9`={8OoexS`&klrq3Dt1h`skkafMH-5eAZ z(y6GYOt7JyhB2T1*5`rj+;+yce$9P5H(GdQ;=hKzM(Vl#2IWZ`sSO=mYa&M3JOjOU z{ha78tgh`iuyO$z{&@cY+jj z=+2s;isfjv)l@7NOPzQV+n#NrhO`s%q*IO~lhb1n^g6h_I_W7YsG8?pSyODBN@1;q z*p0F0gO5@&aDBDRl=sQ6z8c$TdUjc9?UnV8B%$Bt!yQ@w0Er5Y=yz>!JweFrtKXq2 zd`RfO4e4uYBUbng!+A4N32#?7mV1N$C?yRQ-9bFW6 znrmfE!>R5x?NasXDR-#MXy=kJIN$bTWN%hn1_R$b>bhGu zuMw?rHig4u^_ zIMPS$75g^Z`VM$}Ug?u(Ss`5=Zn)IM+l)xCT0qr=E; zbT_Ln?K{(z^z-3-v1P1^qM32PXqO}$@P5AftNN|pwpXHinYyhLG<21w0}IO@eNnz( z4l)BAjW?xbb|d@|{vj*VojdYD2jIc*bv;s@qE(C#Mpbzqu66Q1hP_-OOUF=EK}|CO zEXFpU${yk{Mt?4Gpx!|Zu;+Ghk5Y76pQdjzqRePzjyCNigJ?gmI(ySm_MAhJU0&?s zKCg!!7Coj}X@6)7dexN;95q%dn4)v)rNJeBhi)_98c(=e=wd4!s_#oKV_oJ+0)r+# zZLN-1J+p()I@?RrwJ6GJWR24~*a_r)2<&y8*P|y6W}vA419B2`mrVK=0|)Pcs@F}G zlb0v?e~dt#O-#TR#L)*x^&K}(%Y78kwY{ZPsG1p;Lkk>)5&&|boZ#bWKDp3oAS&gu z8&?elyQ2DI!ulF3WnIp@sqZZ;m;V6a`Be090#-Umf&T!C4i-jk#fZQd=C!W0OHUWY zy){KV4&@+u5sJu1al07L(^N)g$s^1lwF{6AV5n9$!QKP^Xr~c2 z4{jO zPCEonN&DzkIZJ-$vfOFsc?KB6F5q#_NZ|hfeJEC2YN%Lw@s$oTuZ|n@)H0H6c>u{l zp8SnmAW?%O)7{8vaIM0+qQP$!YLUF{B!VcTF@#;%1cS*Z8P1_axJYB;_@5}q_0j9S zFTqe*sEZP5tn?@tw&RwIdFTHCUZ7g(#McURAYor~XV_~RivuZt$^P>l$OOv^p)A)4 za%r}Q3y(C6;{%>F%sO`KRZ?jywr!Md^G;4zKA&9^>HA~a=@lSsIpJ`4Z1bHoP*Lr3 z1j=_d(ir+2{{U@f-K}lpiIa~}j%Ppq(UcL``ew;Z1jbF(b4a^}NKmQ>{@S3CYNe)X zxZ`GuHdJ@V3eB7Y_rb^8SBLFIaC(lTr?VoTF4CJM=kUt^05d84v8rFE`i5?wH%Jzi z8k&2p1h)E!r5OM=3dp|BjH=3hn%~lII%Ox5{{Xq^{+(XkIQfyO+_=LxI3t1YuAb`; z9xmNe;Qs(icz!L>(o)-~DJkV}#QDt%0C?;M>>O*DDccoMzZfmg)N7=?ShrDHEPWen zikc{dHdK*0QV~9nq0AUAnwZ+^&d?z zbZ<-5^>x)~Y05<$YGjgGWGbqol5^WakjEAsqYoiJBIh~#>fGoZogdQ;)R#(e@paA~ zAP{YiGNb|qIV9%;*BW2y8k481+4mj<%&Z1%c$=>FiVFq42p-@nB1{BYYFBbs)xK;D zeL>EBHJ^um5K;KY)Y04R?vPOP=2g^FIFs9KQJ@)bsce08PpA4i`PVgH5pkXAD46aZ zVvy1d|!494jy)9>PajT6@ZO*Cp)zstAxAG|TMc{pPX30E7hnRNY9_@`nrezVUOCtft z9Pl*?_j|L_L`>Bw8Qbg2G5c#wE(T`mC|_ujB)FNYLPs*~gnZZH{ySvtzyEWSh)DrPEN;Pbcb>&~l_j$w*(#Bvcs7**UkKdzw3 zLc7~|zyohQ=yet5I@uXSgDHh0KKe0LRDz!|5>7I_=bb?r>UK)OHx`nf5+Gs}0m&bT z>P52jR)HOyu>fTM08jGP6UPulp=myL`I)njr1msmSMl@72ToP&vLukZ6)d^q z^%_;VQVKX}*$5+ZE<22#bC1_j*~H}o%*GpfR>bn#&){YO5AynqdyRUzWcXnmj591n zhuj`V>#1EWSrkz`RXI}9FKZ(~Hb++2|HCcKYrP}jt2gqV^hXeUf`RNvus}h!sS}N)qh{(HX zt2g|I*_GqV;W?qwW@Y zI-1`rwHvpah}egro9=m4C)nU=E2uiATZCpMhiUrR5RDxe}GGI53sr)ejH`RcH{ z_1{r+ZEUS%yh$Y;LlkIgKy}IPk}32fBiM~SMw`?-M-d%HscBv0aRPP!08&$2=JO`2 zsPpNRqe&!Ty?gE);QQ*TxJhoFRx-92_jFt_`u%l2`_vLwT%@g~ruaD+w&-F%DudlX zJ=aUsRZ+}#%@EE$M?tioxYwZyD;q@f4DD+dY3XgtD-%ytvoF|&{WTQR2=mA|`s)Jn zAuN(Vehzi!o!tEAKU1L`ONnH3=T1cT_*Q9vjbA4^dA0Op@?2%ET4M3En2hWsH?SW1 zNTD^0bB;BS2Yvz9cz(^E7NzpfPkz4<@U+==L&EO&I?mK;4JsLY%zEf;WuBgVK%F1Z zw0#@W0{34ORJBGgHfJLqS^=GL--(@PFJAS0k4;%5w2?~52;6auo^{xNwMSQ0SJW$fuN9z=L{$txd$6Pf-WUR2>4L#18p=jUd+z7z?XkSguj@G%!@fg(C zRYPic{`0Rq9kVw@_$_?umrb%w3y9koCj$fe>ySJ|s*-!NEU%Yiy}8aw)$7;4v>m#$ z!<%o1oiRZyOA3apsCME)2W4NW)e|kLuJvZFuX>jwlkKDFbhe9H+XgaV10uSxJOTY+ zDou+@$^Ij#wbfymK!XZBy)^K>4No;WFjY1Nm9E_*bD)&Tv&>RS1Zn-HPcf=*tf_TtqSE(dz^)kuz(cet0Gnt&Xm23em%Z1{Wxk?5E5svy(z1!($ zQ?PgaG&0-!#Xt^o$FJW<1uU|~^8=1K(t}O3<~939Mhvbm)-VvdCQf?+s_yY`tRR(% zhXiW;t+R;+2x1O&y>#jLyqpMu$Rv$FO@_FI=q!}Q4IM)e84m}a*E*b1ZQfY~4EGv! zHU&aTt~l&8GUG!PIA&}q1b(_`+rDQSw(}ROBSohnj!En^m0b}s9itxHX|m;1r2ha9 z7zE&*D%VxUxnf3f?ZDBgTyqw+^CQQAHj&8p)|aXB+1;BQ=w)rxhaJjUj%MXPa_0X4@NZN-d-$NRCWI1Lh^Va%iZUO9jbKhK7%)2H8lEi=S zskGc#H@|8{i!0reg{{THq zEFEx1e&1bd0AQmG@yDk+g?vjZaXtuRJgau^uR6km{wN1MvzcoI0*p(TDA`#O-u;=ApZ&vr#sU&%vA}J*Q z04V2KMNc(Z!Y&YST=pO8bv_zohY`dHAI!w^bqa;Mh?WR**_vsn0*^9G7WCt`r9~Sw zio}M&?cZC%;Yb4kPi;o4u7-*r6+=qhmp}Q&iMJg^_cezTRobjlL4Xv=E<%Cn_0Xy( zjL*KPvoCZZiPnmO*{v(}gLTROvK)*S`UO5@i^OFpR988iU z85GJt4+mRnAP~OB&OqGQ^d0pgg;tuB*2~MjQWZX@)sN6>GfX3AmCo;!o_%x^$%Dyb zK6O>Hy2p5)&2fjys)x&GOy>cRGV}Ee4ugPB03GyAD6Y5q-!cefshXe%XD2wv%aVNu z9=_VJI%mR!^1pap)vAbPSn1<9S9Tx)p6%c1tL_yqyvtdtuS}pZ=i*ip5~Ime!uR@} zIrN2fD^yd4bxETbD%lz1xcAhL6nY-Nr0;agExY03tf&4MRKf?30l@oZe_brwC~Ka& z2&f^FYNlXgjwRg0{RjE#11+Et2Wd@BIqF(dwAB+MmnJoAG-{_MGn2=s-|MfvO;1UA zN~)?_cBPJ%5R}oNB~TN}1ZyhF z`+X&vx`10+CzS+hSe=B7=K%5t_d2;yRH>eIU8`{@8u;W|sUVU)ix}<({afI&|^oWu1kG6f^|bzQ5f@8h73@i zLg)NV-1`goMUMQidAO0z) zRd47{u)IX6ahW!i*h-LlNJS zARLn1fIl;#<8_<=027?$g9GAc()}ZP>s#D5D-|)4X$JWuAcTf7!FN7`)9P?F)!i51 zm1UCCMQpHi$(pHTaZynzD$`5bE>9T;fCmFO`sruEc|ZEr+a;Qs3VCblipfT%M&`O{+LmU$x2OMqey~Z?_wBMXB+XYp$ z>l5l6jzpL>SU7y2UXlo@IFNhMt@ISQk9-&PlmzqHLwZ9Eh<>S`{ZurHdF^shIP(tyBujOalC<2!cx zYn-qCC%;j_QaVbS>v>q`2BD*jBn$k?JjZ?bEDLP|pIs7!b*ndj#8%}(cIlL#%+b{^ zUuL6wO-!|GR>c_4Ds2mbODZy)5X{7!^T%#RtBSt1tFTwiJtaV%w5dgR`I&MsPJOY) zJ+%|8`sb^9isLl5dX|P#Ldh8zAwb%{Uj_kC6^ME-dAFi6M84l(= zOVgwa?zUu-%_jP)j2Wt2$VdPH53uz64P_Ma)3}-^D9Ss7`s?I}B%e;)dTUram>?M^ zQ=Ib|I5CRfRV8FiG(RM;;K-`S@c#gedXHoIYFw9jWQWL^nbaN*dk~tcdZARp1%Z48?&tvbR^_KY}gLEB^OD;Qqrh}O9cNWHVO4(nLElxh+ z1XM*eBa{+0-$D+VdTst1*e7-c>I=w29PkKL{>`EF6w6Z-q2^TDG6BwW>5WXFRfgOV zik3{KJLKn|>U83^24U}iQm`k%BURSMxZoCipIj+AiC0YW#z+~$=NfA8(!&=~b#xZm zwPOjTtC~QdlZAoEKa&N}JA_7pV;djhMc?bBp5g0yW3U1Ka)9k%1Y9r{JBEAbPaOkf zOZ3$APgs=EFU32|NC7sgu+DwR1M|{?paU#B_WuA~8laoYp^lpXm)^kom1e;Q{{X4e zL4fWPQpKypezT>fo?Uo6SUS`9`0${NjsH>ArmBN+MRx-m@6fURgR{%kXP#c18!z-SS`Sz#Qte>bmRPQBcWW5?n3S(2D9RiD~?kRZi@| zWaE%d)5!WBMv@n+9CQ49{*n#$UfB(hC3_@3Lk+-%UODZJ7^mr)x}Pnoo5+dr>AF^5 z`B&4AQ;iy?i>4_0ek-hIN$!mX z*pN=(*Z^Y$=US~c`6}Z`1v>meEJG8FDeR}y{{Ve7c%W<@EhRj(w9{23&PSGwH`{8De) zK*2l`J7^F44}0qS&0XHfPHSsw9T{mMO{O9ERYA@G&fFFwom+kq_%+m?4fu^_@ngmj z6_#5hk8!qGzAY!X-6{eR8oEMQK?ddp+0IA=0CTGv!>^>@AJ%Q@t){9n8?NHb)hp*o zV4k8$IYYU|_CmdgCy~g;G>_GE6%`ihnQ3X+-a(R)F5pkDKfX0@b!D3E4dS{w)Q)2; zhGQiho@7nt9sd9?8SdSWwls-ZNm*~1plE6k$&wM(lpkin{)bYTUMHPPnO{BPQ?wfr zpm~aah6yY69{M7)R8~wVX`>8eaFMV0obU#$5B4y19cPr3O&r@9guomq9;|VIzf;DS zY3(v})qB*=-fXn;6-NNz0pA}}sdKxTtTjn!&zS4fehc1uJEd&>eARN@t=AmWRL`?& zP*ws*l-%U20hMeFo<8YPN+iT^1O5N1lk`Y;^poqS5^UZn8i{a0tGqHi*qrRj{U+^Y`6f#^mEKDpLa8izKWHOJlK@+Efq zgfR2R@r8Unf(lBvzSc}OQG&4-#|}mb{D=3^y3Vwk^Dr>Ef|{MnBV+>12qBb?LGP;K zy7M|xM`xpU+kn!aR%6M}=bb})+;pS^RmyRCI`@W%TA5y&)Ng5Y<9}K z12oyq)+0Y zBzI+!DbW2u_tz(Xw%$VRi3{gB$j9~7-QlZE;>Yai;uQ=fq3SOTYT)XdRM8T$d<9aN zs-p}!;#7^tx6SXQHTo45lAHs<{yu+!#Ai=c=)s$vy=E+a)zm#XPvd3n`dtwz>I8CD zc|eFwcmDvi1oYvE+&vhduDAZrc21s)siy0Gx;|e`^xOizwWzYbM%$9$A?1yIEHVoMn$EjdP_!D!U!5F`oKt=zj=0j-g@dD!K)? zr981s0IQLOOym-GMF<^%T$0DJ9BLnkR)({6HB>-`Nl(NkQ*KsIo3ZVJPwA!oT1}?9 zdj9~4ov6HBsNI~C7NA^CyQB!c%VRjv*ly@)nubhZ#yp~Zy}thd>80gm!6m|xkC)Vp z>vJfSNd-HX^2vCZj1oOYJw5*bBda3YaYo60J{=rOa{2*xVP_8T;c{pHWhrFKlrF zmbG>SJ#z?4;}@HIr>nA<>78(B6?U# zBn=Hl;0(}64*idh<<3BE#A=M|Yc*Ah>Z=u^2`J@{;wUR=32J(%TkyJ`-G`@R!O(kE z_W9Dauyt*v)sp*;RZya+_)I{{MQpll_M38M{Vcg&!PDdK$PJ;0Y)6{pF z4@^TP95w6a%|OSBSrv1I_gv%y-``hHL|>B7lGGZwT+JWGDwFtkj32je*ICbN(`FcR zG`^i>YK1&c&o3W5EV=kg;vIKPRK)8@%C%P4ZdvJHJ2HLO8&BKopq(Akmwur!$tp^e ztbc~Va?*y)$#33W{bcHu+64DyR*UR5ZYPmE{-qkH!=H4e8s8*SNgRqpA;AQ9 z`)llszDo(DL`CN(jOqUXsjc>epq`?RP--d35;^&STz4nXld6*ATTwijc>w^Qlnw#c zqo-7sChsz74RM*zbHnVTj!Bu06^?LEupYWt>Mo+Kt&%zcSnp3wkfjLNGm8{{W!W zTisN%&{(QxLZ(X%yaVsHExGsf)sf(>5`0Az@+y@tB~un059D0`09k=`>HEi??_XEdnq z`bCQmPhLD{@ewTaz_m@{rKX--Zt~(ByPvZE0G6x25c(>grTT*9Lu{#O;G(LQADM95 zRO5I3wcKB^e}cCTp6E*75qgH0!E@?38Y&+!e9%%3LwyULe!y#!d{m{awDtY!-*Ba6 zm{v@?Uq3J>zB`ibZV2lWkY1{0CuCtqBydSRxaYpEPK$1ezRex#;a7FJ*2Kfhg&;_1U>F-h znHV#-rwT{68t2PBvFIgZAcA8dAH;Y@;6dXIMnr&n5|D#k*za49$>ah&m` z^}1S{Hn}D_^y;d%z@URVBcxi(B~f^4p_xMhw9t~wha8C*j1%_N!)L6LkruwDM2x8( zS}PUE`T?GPr&PyFwH1mL5&;(g0Hg*u`smHetfkOO`)D@1yoxHO6>JqAeWq;Q6oJ7rr^wH`1L|biZF=q^-BhO%!2wO40y? z4&>vUef4|v$64CHbW}}59imT?IgUw_91_0cQDI!-LWYGPrmVHoNh(!Il6N>_Nn?+H zZEcgRZV}myWVj0-xAK9k`bwq_r=+Q0;xeR#8_WQ2109#!M5wxc-z>FI8dAmAc~Wu! z9Cp&#KkbG%A9r%SUlS^+{w7NCA(PJ$BPhsb#^H>5dTQIZbsa6r-Az|Y>cXxN_#L-; z{)A~FKMyFOV>EGm=BLh3N$v;q^&0tSv&+-=XQ`x>q#qLJYq24BKREh&YKqUh-7R23 zGO_You@unBBx(8@pgr-WdM10NEmANa%KJ&l+w|1xecuSh zh2z1a-LNCAAD-8>YkhFyEIXJb!|i1s)1Tc)l%&rd_^H2FkpDY3>;(~O#c97FMTt7 zsp@`|s4W%3Gw~_6E3^^n4!%0Z$8KWMQttKb{a^CF5sUO0yUjV?c5%r0W8T3Du%pM~AXA6h__c|M+dOEHv zblxa+ePCUq&5k)qEa%+k-&9XlRoC@@R#)C7R0}N36cPXm4;s_do;P((?uc7zD)9oG zEODGD8bFjq#*#=ucD%+R>^ig-0Ms~<_yrsvn~2-uSioBfrcZ`Byw@A zE_V)>xtM9_XhI^kbKJMzRjm|NQZnQ=GpkpiI%bMo*H2aB3mv)S=tHX?nDeK?g^DQhX1QgOLRr|7dy}nr zJ+scY?NT$7t+B?Dr(8apV+Amdq>WX3U%UfLE!TR7r;jkyE1ry4@^-fl+& zjX;QHz{fe&i(~NS^?B)dZr10gm(G>r@ARfPtGy$^Qh`fG!<-Sp)N5U)y5k{^ijHZDjm2U@#1CHDBDiJV z4-kRIIgr+r$fP(`+%fW=b@6P<-+|ci>x~>VUP#+A6>vWZToJ6}hyjHJpY5&>#|8-A z%hUvH6BYg&@IA(-(p%n|B}tQU9o2uB=-f&+fIu1d_0cLjd8m!&^8Wyb7vf?#A5BA8 zSGX6{wlHGs_edzBFqBDVE3_%)A|AYdi~0>~{qs%b{{RhGwmBFWT^V|666>;_GCQzj z1Y`YlwOIf)GgL>l6M4d@00HTMG)k;uWY(zuCRG=Thf=W*EFWK8LWwFSo6Y%-a(5qJ z&(~PU*vpa!zNEA@RPobIDg{}i^CMl~FkJ9R^v^oGIT(zER2&H@Xk>bSjD5Rvv$)Q2 z?XQx@B5e^8tOf}8&vC7^&$tB#JZBm2t%4?* z8=nN=dYpRecT(5dWa=uK%LP2K+um1L5?{t+;AMwkPbVIk)RwbE_F-KGYMIcnXh~JV z1KbjM`)XyDK9=eGMPF2PUABtzRV*e8EtaBb)g`EA0qUm_6=MuY8wwn6k(1F_6<;;J z??3YmKo&l808X;Y3f4vDJv3k^E5v@lSSeL z2X%zED8zp^03V<}`m>UNkuPWNkg)H>cC%31sFH@sYKk)mnL|k-=3nth$LdKq^cWiI zj|O}(wcW1O{ZV~{Ra$y_C|;FRoYYlGxJB}ga1~hd>Q`KiV7=UEDohl!kku+gh^luu z{{ZcI_Vqt)bytPIv@NHmY_hLatyJ|Ctktp7(m;vv)mQ+ujJOfU8mgpggX(>CldIEM z_3l%dk*8Y4`|m%PANJPp->0m-2hrC^YGAd^1=g+_zDqEUT7NeHcV-48QT00Hh_BTK z38;EXgwfKBEVC2-@i+ZX>Gm49e`#ME@18H#(VOzYOKP_HvQbb(gK9}2!7Rt-cE`#I z`rr*yw#qw%LOL2Nd(;I`@-jE!W8Wn4{{Xg+phweb!dc%j`!250y$2pV2zSH35PF-! zo4LMP_B3;0Dr0k%ah&(=t@ZqlMyy-+iB=yLZgO8}=&jb8g^eP6TqJ@|C8SZ0n3Iyn z>#E*fo4ZQ`E8C=GkQHr&a;LtNFBUtMlBp%8rLKhl-W$tsV`1sI4LMWR&F=0jX-!&` z&JXmLJ-?-%s%a)kL@=hHSx2>HXreKHR}Vl|D6 z#aop-MNNKrE8s!5%o1&xN49%6*#5eAuDDrx?#yk}_0h~7gsZy?By0u&!R&tB^uwyE zV zGt=6C+33-3X&cn@<-?valk>l@K8x$6lE&wAo~ousmFAI-_T-W2{WL6pxJ?>t-o%ce zhmLtgR@BI(TMCjfar@&g`<&^!;3P)MpDC`GMU4 z{{RY+v=VtIx#LxZ^?c@gfh5Z$n33IyX&Cd8NhF>>u7qxEg+rgVoCk0QIi}H|P;;0@ z>s@)eRkdyAu6mi@@kuilWB&l3O;~*w(>+ILw$EKw9+_+70vdN_2ZR3r5u+Hv$87W8 zj&)qprCekdl;hP;Ivv1QI1#mt>r9 zp}h&g;Pdp=KSzCPnyDJCN~Eqrl>S0bA8-bRL$iWDBGikD5_*s&N8VFz2sqp9b&|#9 z;CCmv(OF`T%zTuT^1IOFchr%r?X_K;@2(Gl*^Y9LO6TomCLIZomwauNj(> zLn=to%8nc`I4Z#KK^%ez&UC}%3P{MOr)Wk2VxW>op(jh1NY~1hvI!Doeb26^&Lmdi z*2H_Iy3%#cwtI>dTm1@`xWcP}SqLBeS^@myIF(1y#|3~+sz-*P)02eIuy&prwFrVgby4_FsQY_pD?y>o-?aY#Ni9LC z^1T`J$%)8RQqIIG1sNrepaZ!l15MpE(nhYaY~iDlzDCJ15+SBOp|SXnx$Fj;iYhy{iK9xW^FAGqs~&${ z9qOL0t?EjOYS|-L9ykme6l7!6jQgCOFGm$j@*>m2ER7(_1wwZW<0IT>jOrS-4UlDP z3`R)iMwZnJWXB;^`tmh4)kzyxQ`5~VDmYqyF_$Wea#{ZQCmJgKH`mp)T zyE7H!!B%@InyRw0CPPn4=0yySapsZSf!|J57l|$O*7&W}^QzOJk#gDc-MH7Yj ziaO(Htb|Kd05Y{AvHlZ;JQXe(N$pIi`{uw)wR^?Lkq_pEi4Bo$ft~O z54#hVKYV&?5$SzitXO`<&-0ozC>0uj@*wE0QBMsW6&o02MlCd$+6TA#=S?;`iaH@6 zO8RZdj?MV_!N&*uw2Mb_u~WoWNa`wPWh=daMH%COPbV11Y<+d()HEclYAfO!M(iwj z2hVYxhy=gT}wbTkXQ1WK~w(sNk|4*>XPo5AUIX>~O{snk-|0F&L|^QCKVL$4PY^aJR~> z405?@d1Zx%%#tEDJ;50u=mpcL9(dlH;@v5W7V;I&#U~wxewt9Y-zp`O5ytT*FgF~r z{PZ{C>I-MgKc1Od&O4g5XmQI+W5aI0py-aKqrS7ridjQ2jd^A4N9nGR@h`3X4)NmM zOIg!ba~D*_0@6uEUkQX^+6sf;{o+mQO9k_%Zj)Q8BA4OQ z=10eIpXT;pK?EOBrO_0HLjb!;;k^#8n?2qy5&B8*Q-n9$GRFMVoPwSB{b1iG_NvcddiZ%1c%qk^KaWYopy z!UhQ0xs)HiRfp3Wzkg<1rNgAHo+b4pi38YbI$CQz%i;)BkjfikqaWu7<^vk3s;!PH zDJ`^3BP=m2N96};-cP4+S<&ajBIxomJDc&C*XvUrQBpnva5>ougO66!S{B9AKV% zX|ju~Qmoj|FsNS4+s@JE4(YC*zTcPNtXA#MvAoIUks^i_P&bn#jHn0%XXeka)!EWE z?*w`xni&$lhV_0i4~eL%lg|q-K4{>MER_I)181H&(Vy9~##@L*gvG_zM=h~dR~TUAhy~sQo)j+Ac}}N1DrGYjsQ6B zzMb`5bzfCmdiL128ar&1Q`+0fb&|N$ypc(g7_mg`z=OxA)wkC^9RAInbv&|oVO%;+ zvZc4j4MNJ)#mLD`vbku$=Qv_LvD;LKQ1lm9cw=83UEmQ1ofs~g7K<-1cxcMNWU z&mBc8fh9FW z1o>0Me+w>qkL+?Zh4CvY%<#Cz-0lOuPxS4kPKB*&?V_)xYp0Ek3N>{AeDB+z;>Moi zA3i-Wee_aWPn%b-BmBWVTZT`GFQ>YOzo)LTS5VH>?pv1wAalX{YpD7H4T`? z7At)`bn#SDO0OBFnLb896#0rVjF1LMI_1w3I>Nf?XRoBNv`a-t@Nm`CZ`&l&<#4Jy zpTo{HBd)(_?!4+sSRt#qwIWlvD-|+?1-6yWPFS3Mbg%v^_NQBO9;@nGYh(i_#I-y_ z>gXPxwIjGx%_s7jt{*H0=L6F?AC{|I6&=T`WlFZGN_%uM?3%HU<8$0E9P{hxs(G&$ zi`8MPxK>n3jQMqWn3+8}J^i(DzgBer07>|7Z>6KJo=Woyn5kYq6fhf`Il<@k(X|>^ zpaidyW?iUR_u1dX+u}8UOolB<>+c zp*kyDbF8{mPgPerAm&MAk+2YYlm0_Vw;JWCsy`3JAmT84c?S zPi!51WN4pjsRVFGsMZWZ1~}tsKhss}AHHSBQzX?i7HUl7rKXN08Qeb)r|a8Y5#g1T z(c5Mw5CkZ+;IBK3W9H}XPN`0fwb8@W^fBDq5L>9Aor5VGxJ@=GThOG@1B{Ll8+jMdm&ik5k+SDc^B8R9pH_U??cNb|DETP%2`iyw)z z-1`Bf{{Y#W_I|&9&|PEx+I3Akr$|uMd6jY>l-y@ipCLU;jC{@LIPa}K4EP(?Keb0& zzfs;AqTSK-lWeN4Km9Nt{?7(@EPFF{AANq1^v_ONdNRvL(!C*Sf`)?K7={?)Oe9J4 z_hZQ?)SX`Y*GX5QSKNN%@%SH~?KM7^^xcEiv$HhKK?Erco`w{KB1J(O3?y-ZfTQvN z4RH_ctKrhvy0+idR1|Q=F7UEVC|t0TEPDE65&CPrJ`yTo#WGor=4S3rsQ&=kXnaOe*-l&)$gIP*yet9!%qCXj9HtxW2qt{ z82p5vuCQ|~PQidsJ++XT^Ycs39fxguJ<7dLFM`r-H~{Z_C=K~)Z5`UC-%zzy%Y>A( zArK^F2=w&VP?19+2q%|w7W{{A?W)V5Ekf;DYrYIjh`=dhfPK!c z%XdT2T*vWIBv$-8kPlwjW7|5?sC=1F3FejZ#2)GtV6W$^ zGK!Mj3c_cliWY*H!w@Bcs)6i1$F7C+wT@+hp6Mi23;<(1L3e*nbnLWnOG*k%UB>`2 zWR5?!mGw(}7$j1lh ze#cCU0X@K!AaOFb&mAS+o(UTsbuj~{}hj<)s^~=gPcM zNQfGLGYUtbI-MzNGCHjI|MZVJdrWsC0@)v&r3LrdAy+yz!~;Ew87ME zJ*85vncXp&V{_QAs-L$bRsH(oOVlkDzND+uU0@~$56Z}=JAWcFG*i}PP(tr|lGY}n zuPskeZhtm6Uzn)$C*6JYm*Nf9iU6HOMIy!f!14wH$1oh={d@gJh~I5e-0jfIJnk!D zj6l)?L2-aN{J{EZhwHoK)Yhi8O*DYVBSBFR;ZWr4kY~1e2iT28HE%pZmk0?mdFyCu zB9fIWWTdOw#wis@8@m)??Vn9n9aGlV25*dtwWw}z<~q7{RcIW}w!sS!TV7IHZp@LUf608&2s*wg5iw%mG{ zdcUYMrp4gR9YK9*s?AjU%u<28#q(|VJ6pK=AJkdLVy&Y<}=`q+y@R*=p(F-!>0*@qvli_HZT%nUKC zN=YgYAD(n(lcOz_ie$LDL|qFxM1qt zOOL5~+4`1}HL9u`;*7@{=K)5mo3xZvkcvuH^Cxqe93DQ}&hu`nqHwHBDd!_WB_(B0 zqnvlvR+mJ&bAdAHtP{UElIiK#<9g&1&%U}3_Ic>LMaHhOuIEP_sRc7KtFC#*sQV=- zsFhl-aEQE~d+Pr1V(ymOYnh2MS^}W^duo2HoNXSc239^Tbmg@1X>X=^rU(IIPB1&_+AEvT48|6) zyE0{i(l2)EO6V=r^y~AH$X>%vUMO`ueHUkorma%1#k!F(Uc)+_;oTi4L-e%$Szdu< zblVcJ=RSmccGWA?7b?!GyvtUQ@2GN5 zw;bu8q2PJ0(X^YNXU~)L(6q7|Vm>2=$6=zj>cMS&)WjFD@vllwjF^4F4#;R+TSfr?TnZN+&@ncbZ zPVwT~Op-%wl_q_iz~jET-uL2d>g_D1YLEjS%6n=50HeA`Yr)keKtBr3#7xtNl9@1e8bs%spXq#I6hy4h+~I+$&$ zwWQ0>RZnoLc_nT=0q08>D}?n-Pz|fcZ(T4kvQ|VAXqjao4Z}mKZ5~q}CAY5!AZp2e z4$~;E0m+M`oZ+>a1sKT5_0Me-mekBQDjfYa4hkodaR4|c-_U5H!3-=?CbXt!4KCaw za?S0il{5LSLJTP24G_Fm#TtukD+3aKCLhdnF(YUEDs^PZ88DUdHyMD!p5D6HkAg9s zY)V2%&p&NVg4_-dm!H0`jLBL;C-B14A4t{pW#$C)E|28I zT}Lpd;sBn&oagPXCB0p4GFHb=b%>=!Ir)POHh#LZx{LOO>u#awOIL^1*{SR{TVw(c zBHpDXsu<;_lQ}u(aC||(yc3x{YEHnNUPX0iD>@Sx4#Ro_u5XK=xPgemkI`P zS#YHQC;)%L1aREWD(vg~C)A!Cw9r#kI8N{^vY$Pi5JeWj$DD5@(e5-8Q(iQoaQu<%>HQ4=Bk-$={?oFDkEJD*@X zdh4<7RhHU&dOQ^<$^i-h&fY&k{#woYo~e}jY2(aqOQRMaR?dfs%|1Q&DdGCss&6*9 zA+kNbo#c{8PzcJZ_6k13^E% zCY_~y+~+^j+gc+{iMR}Bjbg;-zO@O-lx|EXLs5*WIASn0I1T)R+mrUtIHV{;4^DJY z*E^=f!bU(l6M%mH{{Wt@EaMS&PIETX)6=}7FgMF1g+j*)nLY99e*9`B>N=a{(Tq+O zl9-xmc5}kUrvLyuea?oLNu{$(D+m@v zX_yie4#0c+Xypt)1xm2U`&m~R#z!3E>CUBeZ4_U+=eSJ7ODk+cj&_dcKYe<1WoN>v z*@Zl3aMW5pVz*vh&3_P&PfrvVRFX;d$4nk_SGxc+kIdup)s@lz0K2a=)s0&k8j=i+ z$jLiSNgaUCwyF<4YII~~+|@3l%=S^~^*Um--=?%um!^@xkZh5O4VJ+;$Ee5UsAme8 ziCa-8B17xermnt3De2*4qk=RCaVTWQS@1ja{k1!#?{u`N)D=jvnHVvc92}2B^wNCS z`Ddtho&@B!S%0S*A=0+`&ZN5Lnp`wed2)m>8m0K-}8oX<^1C$113jUeL*$REw^tcJB3&rY+r{y)j;XQCSq zw8C9a*Pa~G%^Vb6D@A{;eiX}0tR0EqE_-{3d;$HNDQt9Y;#Y|^QKfu}^)S5hhWJ9bCTRc!G3W4%V;ZBp zeetqS6#BASTU4z}(-x8NmfBIkmj^FU{{Z578!Sosdki^S`j&ch`0ULU(*{w3Oj5eu zscRBpRM~kBBam~0@1S%v#u6PQR%AI-_0)7|0M9@$ze@>fn`j5+T=ySsZjz+~B->R- zV5g1$0GIilJ&bT82!Te?ENjBsqv-l?GP*Tt=?i6;mw1%y!5>iwb`ut*b`Y z$_sRrQ~9hYpoM;XkddYpN3Z~P)^f2P>drtXKgt!**52`h;waKhQw2N3o6+PO9D|RP z>O7Rv!A`XWTjpY*lfl5wj$CbKG3LSY2=ll%Vt>m^*9-E+40L8eD#pc?SFj{wInzp7 z%oT$4*tfPtxX1C5C?|LcoxQmyoN62to?8=w-p3uayjCq;Y(YZ98RI3m2j92;XH7QR z>Ept~M@|d4a`TL@>5okQdOH?DF7`Dmj76n|$pdN0WQ8)Pb}12`wsN2M@E6dLA` zsK&^~(0@H@v0KunyL|MrPa0?VW4kxSEx_zw+n&SMzJu6~+V`fX z>DpVg)eE%}a7mBXa3h7ssUuLf1{sr8!oiueTO@+TI$SCi3fO0jsenQZ^zta=AHO5+ zbVkKNO+ik>=AKHN2@X3bJOF;y9^+iCyb+ieyj%*e0!^t5f<}nrj2xVgw;Eclp=!EmVn;<{`5S@mJu~&^ zQq9)p)e~ClX(Ek+ozEC$-P<22^wyarCAj9Eqf_y)t99j^l9Y z>dJcSWX}~%CIDb@o<48L7~;Zl*Tn3}4L7r=~ix8aVD1_8XiOav^k{N@ogF z+#GPckC^eT^>vq9?F@G5t`Z=igvUt|{HPFf6~cqPg9Cm<-gW51dJG|v%zQeVfOz3Jr}n0?f(G88rnG2 z#SxwokpUZRg+Vz+I0c=KIUEh$_|pCTZxnu9Z4^?{#(b!nANXmHO})YIocBJuHCQJO zW7`dbYaD#WewXWRj-!I+y=Bu?S9PmH1R*Mf9#ANLb#F)Cx8~=7Mmg1WZ@Jm6^R)LX zeND;Ge6dGZX=2a(%&J`dfEo81*$tw$yXB0R-=0c<2h;EN(aV2A+N&XkI-2R;vR1>o z;bBGN1&9Rt@<;X86|ES*%rUx}R4;+?{$}exS=Lri+$rfTv=kKc!YJw~X=8H~kUIt& zyvAlNy+_TQ@G*@-s1$o$Dq77?3b)QxKl#R{#X@-}>=&ZMiQr}ZOZ3SEsQ+dxG z5RyJeS6}eRs(Hc2Mmf?I_Ku}0nxWxU5*#+gz*ig)ez_UXu5={ZzYEcepsMQ*!!%da zQ^#2-x0aHeG4hL_n2)YMzKJz7avY%qI1C8vaxAA+P4?tky2bXLgf@S%3d z>=ZHe)CI`QMfE|nA{WY$Jf=b$f3{s*y6;&kiNjT%}-}2D|1ryb+fG&JTPKL44+o)5jE^(}r zN=jQ{a0-IFH0%$29EBeFKAL){wI^6~OWP_PO=8u_Q$ak3=1E8j%FX^4+6O+kI$f%% zf@~}pT6$vC~0liLM~EP8aSD}_1sy} zfW|ZE1A1%iohj(NU89y{K2>8GLn9S*W?#mPIaUt4gAT`5CHmiM>W-wWh+N~PuViT| z)62wjOP|E5IR%Hz-kB#ty|&-#fx%Ij?XBtsVFTHgpTiqfqr^_CrjDk2jYH2%Zg4X! zXc#Pv58++pZ3nprRcBW9{qL$PYp%B`Jj&aV#I*;y#6JvCfx@Wu$9*;R-BsSj)fT(0 z+Tm&HIPI|@h7_t2k*JbeO;{5}6pM+e7=T(A z9f?!MM+`7W(??DfR52MR&-j|3w1ZHgI9~DceoWKR7bc;(OA8VbrYv^ja-4(u6Q`=M zm5{|G1&v`m9l0caUG-Vp?RN?96x8$1Ey@Rr8*yDidsC#r&? zE%Q_}4bLbzD!Bgu=>5HY^lSAiO5pc0b<_v6Iv#N=j}H76zICl#JJ)&iP)Ks^8WM01 zCq1>+T|eNZuA0aG&RlA$YUpB*_)t{4Nf6`l5O@drY0IX1YSS2mQo@iyRz^OwQlkmTY33Q9#0;+=|37TRIhHH*-KFsG}THY zAh$aQ;vjmEHP07&6-B;ASn6S{iNR!+4B)SBRPqjw^w5qAKN&nsq&ady;#j^DOlvMP z^(|BpB6Q`ZrmCDG0f`uGL6P6)ApS#K?R8p;dyMeasacW8aDT;}U;P}?*?c3rTuCv> zc(_AujGpBp21m!!IMp-PbY#yR%`$wzV}H2hAJadkn$jY@*4VuQ0RA2S09?nkjuOF9 z^8{P!B#K3fIGS@e0we{CIpIJ9xykLW-u;_DX2j^LOnq(F)bY#IwbbXzq?uRCw$r-u z8$5ZexzG4V9re#10Q9n2I*!-ZH^_LTs7a|KVUh?xjIXKx00{i`V}8-Uw8h@%6;DI- zy+hH!)AZ%ZYK>#GuRK77LEp=4 z@)%>CHmWOAM`}F(0MF(rZk>09&nLMfscWK6fVNF$;<`%U)io|tLO6z*zFY^L0=W7-2(a2ZX~k>Tmhd`$8CJ-X)9@K zq=xBH6iXCg3$Y<1QN{*4pP=ucwywUR>G>L}j*6b)TFAm9KH(iZe~$z4k?eTT4yEc^ zoAp_iqOM8RM$~5*{PFLm8A}|IEnbn?fg_0?`F;4!^mGNIq!`FNG=z3$_WBJ7f^r+n ze#g}MXI^m|H_D`a3Dtw)&w)L6;!2A9Z52HYp(R-7xz|jwK@caBazSi;HDX9&#U|_@b->YqVVe0^>nc+to3ubcMMwqk3soqPuuHWqF3EosbE3zIo7fwv4f6P z&VI+XpgCYNasWBW8Nt_JTRb@MV(ogSrKqw=6&zI2tyELWP{}A?oa7!k?WE46{gdUI zX=?g*N{Hy?FC{p35Bxq)e_U!S`mGK3@%qkJ0dhGXiOh4w9IQg;%)viH^wjQ`==-Nu z-)QXC$d%f86bHQkbD!xgUYoO0QCera-5Db_)0>q; z%yH$?!hE`9IrTetW9_GJ4F3RSzOMbEFHv;|hyt^xd_SX*OHWB6`E?gloR;^FGw=9_ z?WGGZjeR52Rdsi3{f@S>syTO3(o!ILc2yXSXD%_dN1z?~)s1og0Puv?RND#Zj);mn z#T!7aa`2`HV5kNMx4yjAmqDv6Ul=FOdW`StvX&zEfzDYUz>s3CGgk_k24rD*o z7dSvaoc$j0JNCBl!==o$cAM=T#-UrhQf^RwnD4Gx@iX?xuIlceuj!cW&@>f*pqr5D zr1CI0I_2uoa7Hat1hJgwE`)!5M1U*RMY{wR(O2m@WIKq-5DjIwWV3PbBXu=JO?=2# zWFlt@9%kBrpHh7WqOIX(o$1r(bvsT62h)S87cHN*sEJDu>XgOT{{X6P!&c?Lrz47s;#N1Tb0X}CTVVY^oAuU4sTREF z%=J1aE&y3~*dnX0nkeaJgMQFi)&6n!9nMF-n$xEj%?6@Wz-~juxD)BY{{Sr`bPeXm z(#cEltSF#)L=5sNlWXUY_{mTY=rwb|t5)G=5tt$wNYs*Cp3FX+_SPA4#LZH>F$7!c zWVb~krM>o`3S^D4pPHZY(HnL9$d$s>d1yT{8)vU0+eYKF&`C*C@$khGy8wQEKQFG5 zx`x^<{$*9A43jAN3;dz*I}`qToDcyr0aoHL-DMTA#akS-(+$UQP%?ip*!}oAUbs*D zhB+?O6p~34z?o^)_bUP48@~rWowT)DxZ5!s*H^tP zk*P@}rNC@2OB2OJixlo2|WlS9!HbTC|TGPS{AB0#zXP$jEL6o;p*gt(`+Z zlOe@GMMJxQeL)(byo&oZMPo&Jj;4voo}MrU8T~L8X~!rWF+G8PZmaGQI|PeRaKVl#@&!YKD?Au%BK#X~kvkBtsE{FuBo3H4{26*)ojF z^&Y>N()DAJ7n9~l1HRCGRk8R_+k!N18_hSx8fuUkae?eUheEFu@y2Bk9PeQ&k9Hlv z`u_l~n??o)7~sM5*VMk)yqdWb)zhO$+=3lJ+W7wf&s|g(MvtOosi}@{jkS5Asx@93 zFi*|5*n`eA;~Ugc-X*A^h>B*D&W%XO1-}pn*z?A!e;EbF(@i;GJ{GzHu}Ft&qvwo{ z#9(0QnwQl(45Kit77{Osy?aqhWF19fxGU1WHsOc?y#e;mZ*NUg9wBu^eML_PSI~&k zJDfpoRGHkZARArOcO2l5I}&sN^^MH*wSP)gy3Gu7kO}-iKP#VnFdwd~>s|J)jLksL z{1<;QIL^k#4}Rc|2=&&+n@j0%-Gec8THWAn>mSxdy41}>F=~0)p>kPSSm1ktKgj9~ zRmy5ox|%hDrZbj|58+Go0R9v7>~%iwu*|D83B~QN^8wIK6b!^ofla2O+JvFAAYYhB;qt`OnMx0p!Bm-41PH6q_f1P=>c zIr4Ug@)rySeTRR~QtvNjq^gU`f#zDR;e>}932qw$k;bX#65w%|mvF3-t`M&#aodh` zAIclIk&;JzXoPgqPS{mE>(vy(oNSSBN2Za%BPJrO6WS(r*dAGx0O!62uKxfIZPj$P z8;yBVBl60Q4<{c@R2?lWEncD2vZ%=#>04^j)A(O3@RUZ&9itgMYai=QQjote50d9T z1m{l_XPQT$S!CUXAZOEBd_A(qc)e3njIksWfsw|e^~8@^B{L9CGCOJK!#X7C?wzjc zsn=p7 z5#0NaO*2`mXzz5U++!&Fdy)Qn33Rr-6wHmt^gjBnZ?Nn!WIImNBPB~I$vw3)p5ltj zxc>m#LY-SU^cuu&^d~xLS{!`GyGK*OMnMuV<4%^Z5^1US#;)$i80=1}3vIr7w;Yf$ zr)xJ%d?aS7Zd=njizr51yoT2{pY1Z=r}1{8j*}3nQgo@*o+4c@5s^_}Cd?m{a5S@3 zCKKcYoc0=n`YTj37+;BnXOA?X~u1>5Q zr$coF6o$64+ff`Lav7sw2+~zmBcUV9{PfLsFG3ck#|8^=&g|>rxLjukI^8TmGD+uA zhBM#t(Q}y4!4g`rJcHj;DQX#5DQttrH3kT{CvgKDYIO|+l`da@$}|TU889vYmw$r3 z9ol-t*UfNfktyNI`G;u99rLO;spDH!eO)9)OoCUBa(2Kck@Uuyt(CW%bZs5BzIaTl zwTyBV$UdBE4gTE?1-MI4Jj%I=$tRs>(Z0PwW^9JW2gH=Q3aWw6_lU^?l3GEMbuK6x zOkr>aw^LCWm?-Yv`Zpg>Riba*7{)lyeQpO)_d{83AlNBX5K8l@NgCshF{!K5x2c>( z1wL5z9{&L8s1@`VYka;TK zUoe~i#OlWJBvbNXV>$&Gai4ujpuE&jxRRP;q;fH!VP)Z&8}k~R>WD=8{R1ZZZ! z{23mIZXpY$WOeh)Use1{ncH*7{<@vh^*uR96t^3VB~{L77kH(F6p2S*(#hyDdkrZ) zSe)R780U={>3*@7ram?p?bP+O*C-7&Of!RqOy`0QLC>ht_aS7$5AhiENL)$%Q{pdC zbyYfB-QlF5gl&$FmN6Vi2W+0~ey2bwBg~5P9mRe15=xUR60rnQNeZKZ^w-llP6@^b zbE{Wc);6HTzRU~>U|Rf2zgP;I8?{s$016^<0LNjCWz;@8c!hbiOJkF%D&nD=<(4#U ztt$TjgtvTsHC;7GZ&GpjYgr~n%N_>1(N;NOIV#&V?2@JTIlvu>j2YpJTbZd9$e%E;{4a1Rc>nMm~nXrGGRKW(b8 zTRP{aDQT&qc7KS52^T29fJs1dHn*wn4v=eo8-;E?t>^RkgsRcMV-Dq8aJSc5s~(2o z0g7n*gV_5JeL>XA4^?0KVrgfv-6o=GlVrONRz=Qm%5oTe#)nT^S8<%nRV=d0jBa8{ z)XEs6q?Kx_5g%_VtTGSRAM3`SIK~e%M!>kk4E`fDU2E3Yz~=kZGsSLvd9YI|(-~LT zvH-vzzdqXNj}E$Sw)N9iX`t!IEjHTJY3O4ZCP>uhdW?L4<38uPJnNjTl0ny9Q)ubc zf(va8MNBk|A>#;BWvM?;jU@X|VW4EOxGivRNys-qdNykQog$QL?OQ z$83N}&$hC)x=&G6DqIs0vYN^tQ0*LWB|-a2^>r_Y{uqC-HrJQ;_ffQMTw<~Ju|L3N_izXU$%8~_@8l~#V(|qsP2`flA^L-HP9yLhsqTZdB{)z0Bc3| z#nW^>Ar-5oEOS&>mPf6wo>AsI4@FVJr?QXFIyRoB(blPqliam>s_Rgx{{Td*Dd@~Z zGabcFK-<{sYSEyR&cSETC5}#sEwK38-_?LUg8O+yn>q{j+ftLRO52?CZIx4AMjncTI z{6o%@Jh03|e5G9^2v~ByPy@NuOeU+Qa};$_!rt-7S3h1garKpdO?64~Y>`#PS5+L5 zGS$YVNXCB>9!^KFI;yU=hoV;7DoTj^cPj$QdtryBm(eO#7-I#AC-}$S_te&wKgK_l z3s%=Uo+r4^GLza#7>z`hEP!MOBe@>BBE;>-uA)lBahwfl3<=1_YHhJ9r_3J!oc{op zG{s87Sk;-!BLGMTjOks#0P=E9m^yvFCXPthlF*Pp^qk}RgR0mvGY6?(X6Q~&E}jk1 zVM)iPNzr>P@{Zj?inQw`KrF`uf>`GSpQcY50La2XzcEm9ewy55F)rs(_(;iLO#rAf zJAITCmz-tEb;sj)1`s8})0{LwaRSKG1C=IppM?U9DF+9|$?>{{YTTewqlSt2tAcDzv*!6v+2R zVKk5Cyo!;8Msi6i2Os6emcBmZrQ!itsmUQb5A@O7rEN15k&7&DiGW8W?FUPh)ojJ$ zftg}s$nCox`Yl-`nC6{H;EAfDvZkR&oij3zPQ&!nj-QF8yV9s64gUbwY2#61iR4@b z!1q11I^Q!#$}$LFKtJP6gCAytobboHMM%Mx*b8NRsIPfQ}2VQ>NU zUNU~e)aYIBs*c};vxbT$Re5FE&f+pvbJz^yI$vt)iN?h_B=XX@$^6fKN2UJ&`ZFTE zC6DfNfgCC;3rzISRTUUia?;d>P^1EIR|Mqa10%nEE!5rU5zMK+d7U=1AM(#{%TXe= z&oC=I>CZnboxjUd>AGSc;k@$!8xC9^7w$DHWp#`o<}`wxIKYK%J}7m4veOMMp1L|e z-3%THqk$Sx<`N2)U`9Q-(?uOu?`vblPi+ar-y7LaA)kSG}}ROj&%_88P3 zaR(7=>_+8g`p%nr3tbI!LtOx?M;jdnmA600z~nc&wmzC|>Ar>Ph-gDvEs<73S0|Sw zvA)@c03k?VHiMr{60`J=QPWmd(nr;mk6~T1)y#{%r--IXE&)5fy|K@IFxl=k79@y4 zRX4*%e5I%?B}$l2AY_f9lyT2|YL+Eu+ngAlOLE;rYU^)MM^z;qFHh7`%Orr9#Kum4 z=H~~Hai6Z1T+!8vm}==M)n6|1Z5#3rBc2F9+g&Mt>I-a^N1i&UApP-Du~QgnkD5Sb z1Wf#t!jO5u$@=Q2>#mRg0Ap|RPe)PYwBe?V6XxMq6(E!8__3d+l-=l%8g-Q##`Wq` z7mk$pXa#)|<_)EtNAmOdhtuilp%ina9P2DdkHJY4bG-=Uf9J3|e*i>Gd5wwgxXzMl zspFu#7$b{hZg-D;zN7kRircu%Z%cW!6nV31$18bqhE5qDkC{(n{<=SBdg@%lr)RZBOj}uUso9K}zgZPfrZLsVRK9lwPHA_zgI7qV`A~t3iXCwKE z_t87O?%h@dQOO8WIU%_pT`yF;WVA{V#&UZcYSq~Nv_XC246}Rnb;`4Hkql~pE)`E8 zdh1+WV(Ayd)W$a8s4=J*JoBKn(0OjsMnJ*N0no?HzcS<>TxjCM*^MriwjE4*ww9i@ zO1q^kQD!v=QWCuFbtTSygAe}uLi&CN^C~My!GQ#7Px#-CKEN^26PI7F*V|uGrQ(26 z5CmRlnUK8a82;J|l12p3S1VViCqm@Ba4+)^z58m>v(QghM2`v*s0x$x#~+@nD}E=f z^CKe>p*i}Kf%*Rcrn&>8Y1W^k)a@+usC6BRcNzT;I!{#FE6?U+>o_0@UQt+GI)ay| zz8YCg4Lcxwe6Wq!Q_rsB$LFcFcKdA&ioe8*-O$lb6u?^78G5-vYU!#ENG(3x?^PF?>dUoRj7eEElDJd=eBW+8GzQ%;S^Sqj!<6j% zA4C5Dp89X6=)x33EpGQDs%yy1 zJ<&Z0EnVIVENQx?tOu-g9K#2q5BZpKb=f@$KxpWxD2d%qNQ)XSeozO~(_Ho7Y}Au= z14@-F)#SX>25qd~;<8C-b3){}L2Sk&Gv4J1?$J4He=6@+29d>(#mXIb=B z+KQ~tCsR~BQy?t{)C{iJ;OL5 z)~nP4>;r)odN!sAmIddirjpAAon}^{rl^{x1p^pqF`f_5dg*=}T$hShsCo%!q@#G| zm4Y3rGl8GJbY9ESmu|4UrLx&aB}0c1#Tfod2Aopw)8cqwbJ_-9kl!Wg8gy1>s_ zP^*Gw3UD$zC^#Rf()MbTNl-){=KlaN6zSf~mGS0trN6ALeFthahsZaYs(1KM$|V%h z45?tFKg7>%+yU>Zr>^Je%K2Rl%963feqvlH$-!~~2txqGMozlCgt12s= zk}1NhGDk-ljua?1;TO<+zPz6LQ>*fsHsqgEll0N*(jqW#K4W@%0LL=QzxdrX^PqZH z!+9v-r?qsPQpW<&$K`xWKPFjy42PVzu5>q|`reK^%rw^}q_P3JLB7 zrAN=>W|c6@_V1CSJ_vO^&coC7mMeqQRml|p0K6$=iz1*L0Ks3rR1L~LgnH|ud{FV< z!!D@s@{*sa?DY3;p^h;GQZzt$@s36z%9>pXc!$A63$N zM3hp)y!n(BqxMGWJO2Pc*LBUa#5(E-ZO{j)YTJ6t7?afpmO~tU29~;S_HXHG%GeFx z#mg;KES$31qh^yHfccmU`S#V5ZR)yPZ$h=2j}){{k~=_-c8$ZoC!JF^zMbm2d-bZi zAvHY}>PC)|vQ=YGJ1N0T`LG?N0Ybo@%Tvh!|+4G#m z)|A(9;Hh2+oH1+gw((nghLZH67$GOj5uBWN<4(4(4{S#0R%j-qq9_!=(vs@naK{{D zKAP&w&2NX6h$fn~txFVWUGnRIqt`mDdV|Fu2&m#}sydoOQ5G3QKsNRE7}G!ay-V;< zAI4B=bech(!}#JLQuJ?3+d6`IWU|!UXzCz8J1HbN9G~f;_p97%YFi{0c@-4`!0ZCB zQ~v;cZN2#8(k&b`5!+&+_@WFGOyFSu0E>>pNM0^@$J3VkJsk73GR+Vrw#+w^p5xqT z>GZ1=!sbxwsR-VQAHaOT#)`>onzHBDba8wqmg>yog+D{@^wKX-{?UF3(RBT9PEd3g z#8%Qs(a_wBADSc23)y-4X*0!d+HSkAx=?g&C8i2XZdKl0xteBPK_}C{KKi1iMXCkM z08ej!*IKlFDG_(u$@t8+pEQHBfPWnG9B$qy#c*Dp9RWAcd@5I*`CFBX}InkedM zC1aDZ_bB|d73rv!lr#$KFgPlC^zW?!No*6y@215ly$nj4t8Sxx9qLr!rb#1Vg_b_Y z{-NAx^)2S0aMcokKf>MhUQHjC5uqhnlm;2b4vJeVr+A7SJmug~{*7*GzWnoDa8o zImQkS4vXrB6E*9$gOny?U*TQKxmt&&Dwe)kzF6s;g^`=n2eI|iEVe7ROxILfZ}!@Y zeRV0sl=U%~7%E*da|lW z<$99o-J?b4`s!DRz7BOyg?&o)TlFB88Cbm)burC7TwaeG{{VG7f^_52oigo->K-E>PT3))fec~RTwzA?Mt-=*{ta6m8e7b@hD((2RY@2- zDP@6_I*@qe<3C*^Q^z7j8q(JYiw;iB!}HTGM)d))RaAU263IIlCAxYr`h~MrH+H*E;iX@xWKLpBLbv< z$`|@^s_Uz!OaA~#B^93Z^!JG{QldGJ0ZvZS+diD@UrY5&cf^W{rdeqxqL2w{RxnY? zToSnKLG}9UCus}5YXBJSGV`g7&yqQoDO1gj*li@Abvyz+bU&zCsUY(0Y*ET? zc90G*I0HVX-&N&zS6%CB;kHrLGW;;fh(8qrfs>xc*HQY1tLkQ!o2Tj|nEB;)vNs2B ze37B9;(3-e?dBS_UaZ%8`fCjRKSV1fFC>)_oux?Qepj*2e?#l7{X=H0wOgf;=@z1T z+|%1qF}V?k<5xbT)Hl~i@?LGdD|5BpWW2RB@yhh!DbTca3gmGv23OeUvDK2@PWLyX zooLWe$BpQX!-M|-(qqtoM{YF07+jbp;r+6#tEj56Q^{2&x8bV;hFIcUBLZ+w2O00% zTkY~wUlx{G=B%bIJZ&t51;U2G4Y#=*bEdATvqwEqj+BE-3mwZQK@3kgI!b~mthLEX zYRq=}*BeXWj4lbsKj3cPL#rknV0R^$uQKtoMO$ftHHNM_mVh!D2m{PR+aP^Cb*}Gx z5IjM5L_=-z!sl?ubZ)?d=zh8z(A5%FK|Hd_jld#?Iav_=po82HMx-<~G%O~K(x#DQ zD^(1`4;ByO2h~q+rmmHoaThiZFm(@6ipi=f8g^Nzrt;=SJ<0z7A~BDs#++*IRMqyV zYN@0Jh9wz4%r#f_r@&k9AG~O!+Vv4Bbz_e?J#*+!^*ULPt}1L5lH2CpQ)-)ZktBqj zpz$K-xZvbz%4;Cz0@4V~w^}%)cWN!ZWWk)m=N$JSA48tnqC9S{hVN@yDCZ(aie-hi z_LUBHu=LL${YH*bb){Vr#Am2!L~=4yO6`_0G)q<1E890&Y>Ao&UN0|~v z0oC!s{{ZccOPyvAqy}I@f~8fr0iRt)@@r>wiU6gU<8FEP$Njak;?DG`Br)u$jPhgt zb@%*_rmO}e6PcAMsDm&HkdmRvj1Y1^#H=)Sk?xf+yTc7UkrG4iP{6O$f1iB^dU>lc zUnbh23XR@c?T`$1BzMM%*{^o`a`|sC802l^gZ(}9UZUAgJ4a6q*ih23^AW0{a#ucp z0oy+Mon;VG%CMSGg{nyctknD~FdT;C9E_2jQGpynDC8dy7gY46PeN6SYK8`E5_FM$ zn3xSjlY=961KUma+r`tw3-t9h49ta?oPR3m3#ihX3YMp*Q-E=jbK6<9Z`c*jF%3Eb z`7%dNRkRYBGJ9&p9w1fQ?JI8*k0w)wQh3r8ijs_5WT=)xCCCZ}qu*rO(MGZp&PJN4_=avw<|<8+nb^X82f)>i8#&>s8A3YGv}@1TP)+l#D5}v8B)X^M!s}ioLy=m zlgeWfHd{J#>i7{Rjvpi{1yoQ521k7>s|9c(7uqwnFW6wHtEfc^PUC=atUOq^8%ojy zlW~!EX|~N36}pg2LhtSldD16{6|Z}(iZ+m)tB!PrpLM#RSD^u>vwg#kB6YPZYU+{- zRCy$J04n#@jTIVLsgP8N=da^&#za3sp6BbKHb@6g#;r~RR8BBky8-Mqw)atSiZ3~# z+mbPn>8EumeXq0(EOOgDQ?+E-FO{p0<1p`n8yXW;Q&CiTmAM(q9Opr0x>Ro)*kkFf zEqJI3e4~tvXB_@os%e=zkhHWd4Y?*_hXZ$3%6E1QbkJ%B_TkYz4cvHRyb5%yvp5Mu-GP7Y2gquqOaob z+ZuyM0I!BxRzS>GC_Rpb%UvX@Lkyp$rdlp7LsGMfpG`fpUCZ}IDYtfJpfLbU zcjrUukcj~~?fPhtUPAGx)sg(0vGvuI^B6|KkT87?w@14i6O-$&MFYxC)i@lVrlbVh z*b%Fi#}JIbO-Ve_a62DdbFZb{fPa{Rd-`i~#YA#xN9>nccAqD{{GV^i}n{@Tsqf<9^(1CRJ|oS)44YCFhXhF4;*;ZfLX zpk{FAsWMAUat+H?G3YUZS8vEyuAwgGv^bl8$jHC0Pg~Xz!2m z(aEk!Gci6h!X{Ir*Ok;okpIsH`j;5iq-PSwxvKyVEp@P=Y zO}opek8mulf(Qo~I3VLn(cC0}{{Rf*{8;t(8qw!~#4bS39{AFBC#o?0LHb$lG8s~m-DU8brt9eJ(n-FSf=L!ZPo7y7iQ+JF6frH1=SsIL4^UgTi;b?PAct!bHz+^# zDI;6yCu1-G;ZMIhVxVp7{{YglBOESy(WzHZ0P*wOEt-a}k~0@NGDsonY6+&3Y->7} zAmfjx*H(S*=(UWEy`LrU8MhFp93RV7uTs)}>aG_U<`KI}t`UX{9OsO7I&6k$>n-&4 zaZabq8<-E}BzF7h#rI+-tsbFUhelvzkWb}QoJS$dZ`cFubuJkv4g4%PI7S4Xc-O;C z9aSqM3=#%?aj5B7)SGE)90o1JWATqpef7?yMMt=C4bfFf?9??`36GGI%8yQU1FRFR z>#WdOt=(&Sth7)+oeiRT_b|ZwN`{Fuv>XyUXPs-2nP!efm4dM#WOn@YV%u-3x>ZFL zJZ49kwqR%GW81gqrCcZ6M^Kem^)^?`s3~N;Ey5@xYJpTHMMP&{e8Rv1&(!IvYjRaz zu2NLhgGn5P8G>;;NeHz*#GGQk_iGd?FC%iLqwdSgxu?E)|X z0AtM7EwnH$>iX0S4t%sf-0O8MWdt+EDGtrd7bQquPpS6Mc0OSC8u-~!jAQAZbvtw1 zv8Qmu80B)B8+7vtOlDPLff2zO9=Z053GS+%I)_>C%pZE&{1<$Y2ldbTZpW)79OsQjD5d z45|MBQ%I46`ELH&-+OhbxRXzpAyyH$*W3@!Q!1yWvRS3EQtXnht5J`+H{x(7ZP$N_Wf!w#>&}hutL?bX!%olf<)K)PA0CSH)^!$c32jTq3$X&n=-Ls<- zTjY>5iyJUJk_Ms}{FVfJ^Tw==fH4VOfB*!Fh4*Y&ozM6{)-5eoe6Xc{p1P9K#)VrS zKGTH{w*J5*;rgPlr_ z-BT#s;6$YNW6$ZT;Fu;j~_uPr@2L_s8GZurI( zyPR#u*lC-f`j)r%ycS!O@It41(@7>5$e+5F2a*Z-Puw3(RWYs`9C7cXbRAiFp@pd0 zN_xs?aP0+cI?knlz#)&$xE{<0(@|h&LYa%%r3^49c=$wmdS8c2*_x7gB4acRp(281 zW4N&XWx>cjwAa=)dl~8K*etfoRc#V(m1L#^dxA5*Har8*_t!9ZH{<0HG`36Ba8plC zLenjEOp?=7kMM4OVvt}3B<(x{ja}VM;|)Jh*&?t#0i>;sj80XGJMoM%V<(SMoq4-2 z)KR*tM?MeCko8oXAA>5Y8=$at95s+Y?+h~KBb~g(1=ndOJZ{D@^#epY@1w6WR11Xi ztwjxL{6##<%F60aJC1lc&`Y$XswS$VjKdr5Sj5v|m6QX|Y@Bj9(Mk@jlEY6wOWqV9 zt3Vb)M24Nu&GIndBj=It&X(VGiU~bL8p_=Yzg{I{cC(0aGE6s&_CgOHxjL`LHZG=8!ubMe~m3y=)`rLtW~MT{0*Hd zZoAG`&w^Hk*BjMOWB!NJL{{ZTP^$MDeZ+8NkK|%A{d5ysq4x6>2*_|m9MaB5%=yX5 z&b|h6!+=JL(pin(7oOKTzn0}nNyB|U{q?3>Maqqbn?~RakVhbmL1q%$*RL`QFL1zs zduv;+?g&3aqREFt45|ki8jC$6h*dWJdA%*+sfL8^D6uD5=cy{&x(8&$(9G!www zB+<8ZKjo{3uex#in*6ug!y82$3k@s^!JEoLcS_)PC9(iM+E<>A+i9qzsfr-;q<|F0 z2Hbk%kU=~hF;l3lQH;m*+JLl*o1DYlIcZL(EVLHug(Z4G(s^*mB^e#YIR0Nw`XhPh ztK|)ruIWor4bqZ<{JNPba?MWI2M)e~XOq|+Idl~)EpM&8+wP{K;>1wUVls&m$iKzi z{xZMf_QtIL00ulS>I(a$Guq=w`hJ(q{pnd!rIN4ZA>#o4Jp1+<#@AcuAKjee$M}~0 zoh@}l8Tli`$@+qCT6wTicCt^}<GMq@I&&$7*bAn8u~n$qXsuSvC5I# zS}2K73WKP^DO?uh$T7x=AD1%caSTtW9+=bLf}S094~boAA5B;zGDwjw3ATFf8Jv&vB+2QaXQDr{{RSR`X237c9sb0Ybqj-%aJ=r2|x;CvCcA2zOHC2 z(akJDVj&sE@s7)$f4-MIJnDK&-rJ>JGgGvYMht8)aktaheKli6SxaxDp1qk$MGTW< zkTJ1Kcl!-wtv;dLPcfQOw66)&{9*A4o({LZ(MFM|g5^~oD}#YMfC2kyYDbs474`&< zMvis;!EU`rbi68q@l|tFay=eKZ}ZaRGBLs&<2|v)y)Hm8DtW9hnb6v#x>rL?^=Sy6 zRYN4X+L<`X$Dq^Qg3V1v#++?$Ez0gwkmMf!08nwpbfZfS=2yo#3{f3dOV9y3=sD}h#=ke=Q;njEa@iM+c}R1k<5ZLB&0 zjttZKb0XQh@gKUX9+jfAS6(kRxaW@99{J#oJ>`fQ0au)oagV;aew)NU5j!w`B^&Dh(_0@snb#GkQE$758ky+^^w@$EE(u3whJXpd=ROchp zBe2w4FI?HbX`c`3I!5Pjrv2t3AjC148~wbBP%Mi zFPFPK5zc=41=pP!)g38xOFvOq>TT8023A>+sF6X&TZVpo!S_?8`KTg?V8A~+sX35-WC&J-0O9(xhoeRWLVZubklYe`b_ zxK?a#B>MB;Oto%W2A)PHIKXH6EROv-=jEwnS zVyhS>2eBZj2kE88sni=vlln)#pQTmn1ND^EH6$XLWNNi`VnKu>ZX9W)}pCTd%ZOC(}YdnD1HnMmXk2CHeW6%nzEVq|bnf9tIZ zEfy|F#5t**h*Ow-AxUHcCfa=lw6a4LNfXKd8RtvVU8|dsF80+Og=3_N_JhV-BffQf zbcG!R{XDf%Mq`p-LaZb)-aR(=$NmjreM?r<42Y|Yw!sm)0OCL$zlWv;EfqD^oETQ4 z0!Yhe%anJ=(DxsngHt(2SmbfLoG?B0V|6t%nnM(huB-vVuW~&;hgGFi200HIQcluF zgQ3(jrzS0BdEzkIuGKU(G0hx?H;jS`vGn_D+_!kwc!Ge^MFaR}tVaa?CeN)uZx}QQp)zSMqb&OWNu!4%bCfd+oMt;ivnLfw1 zfvi?q2(D}Y0Qv1_p{foitjTT`MilE3k;wj zCuoP%H7Ej+&u7DF*3=3|aYI$P=W z`lAHTd98m)1_C@kTg?=)Q2cMj2#~&2Frfbci07W#7t%d(RX{4Jt;n((i5xjYfHxe0 zf%WH9#p>sAN!K-UI=LlIdyL~v9S0?7xl%nDmEGh}QZ&hLeIHYzM68i3r22!sfEm(0 z9n@XSH8X6ETC{wDIV@b~k&jI`*lFdhq^O{(w-l)pl7r6O8c2V_!;zn^we(#W4lC*{ zNkA$iXjD7p`0U|_^J(_kYl_~6qBy{wK))1^gB;*wenj!x*Ir$lEN7%<7~)kuVtSeF zk<`5;bT!e-BoNY~gOs%O>a4(V#SUpn+r=Oey)X+x+wz9-NxuJ~tqv6%k-(~W9NO}6@rg=Hz4DyZZxtYC5mB|5H>wJqeE`#x7o(J;+GBoWj3{KS`MgF3-e*9S~w#x*z_Sg}@ z+6H;;+;fl5NHc~{{RsG08MCVxqYXoo$Z9k5EWNZq*PN|qyR}xCz}H?$jCp3 zBeq7XUl4kO{{XV{OaPjO;}XcDI03QFf5PDZ0Fl<4hf+V9uO>F04{?x3`5i0X>R`DU z=#ExGs_?kwOM%$?WPeaK>9qBP6U^!_FTjJZ)X>imsDfiHG{7`c5%WhO{#X9l((O_5 z;0jf|#C&bz<@;!@vb3;zE0P3^qeq5Pcl{6fA596mP|pH+$}=8tN}T<(qs2!jnYM;O zI*8zGwoX@`+9w56kt-yKbAkxK(xp6-M<@}=w70KqST;wr)zL)_T_Q}Ykn(IU$&sAk z5OJTc8kBhsxRL0hSX?BV-N_hXz&z!9_djiItf-;N%&fo{D(DX1>F=rV(Ys3+f`l|d zhQOe^Mpv<2-0O9HQcl7e`&5w`K3SZ`GC#scu+d2(3%M$euA0$gjgpR?>J+n?H*b$O|S{3qMWT>TB%7={S%k|G3#(-1hoB{8q z>I5{jVcU$2#C~W?H9nX9=2ne?JG%{QtfCanwm@BYBRUy8<_SHm zh#ds;KFVmxC)dzwO(mX3nMBfzp5Sw-W>5yi0aw^-T1v>kr+n(j%+2u_-p7H&vpQy# zEjIP25@I#y9_L!Rhp2_slqvaRVaOfEgj?#CJ4lwke=&~{!JayWoFq zOVzk!oXn>|xRVJBgo1^bF~a2i&XFo>o_ez;N!0r3>mgH0#DSB8p|tU;$_s#SbUG>u z*@vQ`SS=j-GSORjtc{vBWLy)+ril2bN$|@MgNWA(cJ@4KM^1HYbd*a$M^1s;0Jb^S z&bU})v!v3_rdJ^2Io6HHHVv^QwY`2stzn{8ft}?*NcHcb&kHLP!w+$-vq*A4Yz&c| zXft4K52X$dQ(-}$Mn|r;E=E0lHKWc&=aa38FmMhAs6m$?&4rpi0oGBu1ase7*cKkS z{dJNdQa28D0#NlHdVD=yj#^e5lg$oP59_7t^%8jx<4zG2cPtM#CKB2^yXWqlaSrppZ}38uYNPGRy{h`|In}O!>@>jcXZVN!J0H zeo4umRfT^HO)2`E4SIT}YEmO$oQ@aU@2simXQ_5r0XtNd$Dr1_sCpt7g28F~_u3+?o!#$z2c5IoKog2ZsDJ+e=IeZAEqO(i55@)eH3 zk3rZSD#bNb1ioYvy82`saqM+EJFIeGOc+0Wf#2(;UWPV$naKF~fU<+Tc8VFxO5!ZkEzj=pG~Oz%7i#vZxPJ|S!o)w2dw<_eJr~6dT{|&53OLE^IR_eBSj4Oj z?n%aU%hL4n!BXVpB$1UoW9|KPdf%R9wPYH04l&eZrl>oQr^}zY*QzLDtb~bNd4;}4 zAIp)bl+t9Jk6d%3G=?pOf())P#*RQDVlg$IASh>ZC&EvFRO1D)`Rd~E2dH{>+0^v$ z^$bW;%o`*l`Mo*SRdS>iM9B^n)RGUUI@w^ViYV%Q(5q1z5{$t7t&x&3>!j?Xcq)1^ zlzfos*q^3W&)l9uUFhC97DdKL&u>tDPO6_BdN-u}N3zsWPYP1pV`q_a&?1M$yLLf#s9S6Y2(??ia4)K-*6M z=>4)|4Y}rxc08<55$rpk>!69tn8_i#cGDF_ER;(XG=PnfDFA&&bD-Ks0xzjyfTl-@ zif*V=heisX;QRiX)UmXUv}CWTI+YAjn1FU(3o*gxC)4_BMqz|A9iSc*dTOf|-HwFH zn%*TuSYswfY!9i?3xo?uW2FOx9FwixGc+``HJ93@Um_Fb#OKxV&fo3%{WPgveyy!y z`SOFFcJh1hG`I%a2ptda0*Lmx9t1TkkwqDKrP!VMJdI;Xh=m!_?Y)rRK`ypTt|cic4x=X9bGowk?>c;YH%%GH9>N!i)xCx zpie4Gn_-0Cr(5zRzD&tic0CPV5OihCs%f zXuKTpBEBH+(U&;R30WqLL>PM=PJ*V9TB-G6YX zcYMFh0dUQnoOe0VbAB^`A}{rJZYx<()$~7ASs^o5^zB>-JFr0G^<3ywl|?e0h&<=t zR<(WJ?Q?R^GDMqj0v>d)aOt`_;?Gkm0!cYv#D711T`X`t{{Yfjt#0%C!4W5^S8c5M zX5ay#@*VD2WCPe|O!D+2K~N=z=)P778`qKa&Z2cMOi9zR(n&2qlj4xFjkv%by0-N_ zO-iq*kPz}J;56K!yF%dd{c)~J@u4_k;mUwj2`AY$o2Z^g4xv_NNw?3YVK1> zZ!AqR%0o16zOk|SlzLzsX?OwL_8gsgCUEhc+(*KXVVzObIKdJf1B@8|087``0|2DE zPV<=YA57>8N_L_{_Z4d}mooNBN~I0cC;dCB!UkoW7P zlrL;*w`zF+42K-x&RS>YJtVseX1Oj&x z-y=FsmN>yss7o>Kf7e5&j;5ADJSBr*=WjfEjTJ7y+C&3slK0)7WV?m>to0O8LogAY zs_I5T?oOvec4krMLLIjN0`c7GW|1X(s?^AsRLfxc;2s8#+9}?mx)_t6FXwLjV;Itx zqA}_-ZD@cGff*sN&0QIijdzf7w45Cr>1&nB!z9*sjzl$27!kJ^V%X2vwt_d)M-Htu7e@zbPyTsB(QFE)6+)>DoKwOe`LZu>CTDp+rsXQvq5(1-lwOJr|ACx-RYo> zyvmBUURbbPmC4S1ai^sHq`W3dIcX^=swp~tijrVtXgrw62tuN-;{5Th1gW`HP*gQi zR?g8@%4!}1BLEvX0KDZi2jRw^w#>qXCH4d|G-`T(e z%tX3P2&|TPqGeWMVn=P>T4a%v>T!(-mUnhLj!8My?a|+|w_Wx9m-oL(-#S9G9GY+6 z091~97|QT|gzML@*|V-XVICf(=^2%fjL88KsruzlZEPmLPEN&PA)C-$#YvQ8G*uNl zTow7-Xy;PtEpjA`(;RIi3?4u4t4a?JXsK2zc1d7j=L+NTW6vQ-+nRQRn1F1A_QOfk^o7^IL{h;@UA&1Y&SZ=GEF7UzLGaZ1eJ~w@(|y4 z+o1bwI6WRCVa;l7&qImVT><+X-R!(phXrfR6-%u0Ka#;Vt_Im9)3@~eO)d2Y2_YNilDPFcGg z@DzHTSv?EWmDQKJ3w4&*Zgs6H2;_A>Ty3669=P|^I$Bp|Wrc8mn1S4Bj@8t)eH|n> z3l&+0tpULR932MNvB6Y;w^8cwMg-}enefW*ed?=?J<`<#$!?~SqPuU*S;CEq7<->@ zO;=rAL({e^P0s5jxto$v@EP3TZWvwjRujK($m=T7S$cYC z43kjG@u@}5^amz$h?Lg$JaXDta_Dg zXyqlV0FM*(D&m=zOfp`3Pwj+l;`b1wF+9_-UO_$d-Df!_F+n7fdYYA4&J=O2-daL3-SwLk zrc$Gp_S9!~b}Yw4;`pwf=SA{r< zwNQwa#WfW~l=Gt`ksYkK_En{{+7FKI5X+GxAiot#CM5Zn%9<~CIjH$XVX^4hVns4By}+Pp-|>v zTes)ws*2SSlHssql5jDro1!g~`RPgJb5grVKQHDEpVu(P3|m00ox*g-KwV%FIQPf&)nDQKR<4kOjcI;q-T?`hXADnlf1a8t zE-@tUaH^E7u`%Aev$%uL8uJ#`r`)sAt1R{FP8~JfIF*qQ})#{;tyO| zI$p|a?lg*PDiTPQko?X8>`$on(Vr8#q6=Nhc1Z&VVy6e?RL2Kg-{V(P(_HAODG-pz z3kBXu1K0J@G@3)GxkC;Nwxduq)HA8(NVVRt{vdS>7n&b3DtM&|u@jOPAROzY`bx#r zbQBbp+2v@fDHG0)LNYdn&KP=dFm=!W0J3E))feMYJZ&u-Nw;Hm1apzD&*_*1wG|aI zL6)u|6-ebEPnY`hqwCO{jVtzuo{sF(>c1>x;ccS0TcWCpkS#@447+l1$Ko0X4Tj-C zStTXg%V{=$2C5&Fv}EN;2kZJBGk?4E<#i<<>1Lp-e=a;9nj%3UkETYobv63ma*+b zGFwbiu$DTQ8184tWn@17po|?e)phkfFHkkLl{ZK8Lnv%E-|+oLfpzat+9UXS-Qs!* z*D=Ugf5Ubd#&w}mkZ?pZUIX;PH2GWkwC&6|kahqoX5>2BrH^zlO#bp^#M=#cDbfWxX2^7)DUr_+qC zdml}DT5SXcGtBC0Wn$0`&fRf^1y_^}G`M)=W5Ca$$7A)>AK}!;B^a2+Avhj!7!Bxm z4gfv5{d8gL>MillK~Vsf7R*N@AjW$fXkA?Z^BI(RVTKh;FKK` zh*CvK6cN>YNvVfx#G`CMiQp5S57$>!=2e?L$tz}-hMi|=5u*7}Zf^M>;ve5w3O|pK z!CoRr;Ebe_IVZ8gmFJ&qYIjb>R<{a!CCUnuYWRS)CJZ@`0|AhZ+pgNbEajDZtr?Ohou_(-SnrIMYt)Fce%IR`sgJCJ*4 z(>e)mf--6;U;}Jnnc_1Ae0r!1Fittsyf*%mqv{$~h>2;7ji({mnBxbI!(9*3{t9&M z=89gRs<2BSfs}-jH3X;g5KrP?>N(bat6yH6VS(m5rqS7X_K`Wurs`cHjYqiO1rc1OxyQ&tp??+Qhwx)td zBpG8Pmydy*9><>A%GTbu1$rC!f6Ud6V{SkE{oMP?N& zJ)XfO31dOFPCy;C=jusdCOTz60pyQOX}!EqWnu$?&NRI8vt(+t*lZbtsxpF`QL?|wgOhUgP>g0J4%}G)1hwW`Na;RSZn%&iPl^#MY z%zZEk)|v~P0QtedJ@KJEL3e9}+d=tI>Nl|%F*@lja5#`!b1Yt1IMiGaaC`de1$GaS z-%?dXsq>6uvF)zvX%V(*{{Ss-ra67M&(m7UM!76=jckfgTM7Wd&l=)#Bpyc+U_+2p zYhcFVxZ~<|lv^1?76TfERaC7e)c}Rm0rLPd=Mel^L=5kk#xeoMbTYDdrK)oZZ9s4a zqW=e(htWhNtd5kB8Qdo0$8)1Ex>LE2^wbGyf8Ldl9n3%&_s<%-{$$ko z%9vIyMy;lb3G*c*E4nBSNU?X#{{Ry#$QD@uSpHQ} z*bk<&M2XKnV|w%3UNF+A0jgpr=-S*;kz>kLBOh$%LmGn`)QVe(P9jY zzx`LPl_J{QhVAXCD=dfuq3!umijw2&#q36?$AGGxy zeY~r|J4U<$1A1speJWICmOMHQ#~q0F(DkeGh*Eg$R~ks*9C(iG7L18^^xuvBS8kL^ zSyU?^NR2y1H!9KN8+Fg#BvMlU0JL=%J7p+_n!ZPvLZ5~y3Cjic9-~L;Yg$J5dUe4k zzA}GJYQ9j+D5Ply*$Bv110!6iS~m3@stVYD!vbB#2ai3cn!dzrK!G4$R#w2i+!FmFs7XWJGT(`LWJVVV|a%x}pHL zIkwH_s*tL~)Bp$hXdM{+)kM=1haJA>wxm7UnszQ~a?Cl%$2iiC8pqx`6FSh=S-pA^ zEYSI%GlINhgM+AMh2@cBS0{S_3G~$IDxq!`Wnz5>zR1l&tY?jp@&?j5)yCi@)tP#m z@Am;UJuQ}uY^0@(BB1_boR&XQNjk8+9HZ&(mh(EA=|q*cxY#wkO@j?Dub346DLpcC zk4|+~QpojHAwm{n#~%80w^uE?C5jmQ`jRpexI^!&C1{JaqPmjxC#PR>-MjV8-ryPQ z>&$UK%)rQgY@GA%bfs6+wDmOxDrNJe4cHt5rFW{Vd`)ni?r)e3@CUFOi%W1?Z#Ga! zcQ_5}sW)vHUIfbhoUz=;CLX(2lPbg{QL#%&v|t15J@gCXWO>FxY;dYif3Am1SmleB z{6y!UO>2svBvMRR?r)WT-nyr#Ol>sbL^&!10 zmpa9zppB)KkEdn&pKVx{j*yDYwyw8rMM-X0oPd3Xr4^o5(_Wl&+|kngHzjMsaJ+nx zA>NP`{72ot(@I`1Nfh^(+B%dX2*hGPp*hIaqj0zjeY$GdillJHTOf15*E?M9Pj}@EmvK5ON6m9cS6|?arsE zyk;&&6+p%Z&CheJF|EE#y9}7Zft;URYySW(KslVGV~H&8bqCx7tr^O(`hY*Kx2tT0 z4nLO~icS>de?EVvsJ+3c%-Kx?RMbZT=PY?X!_?@^f+SHIARr86jFa^`i)4wFXR6!4 zU5b9Ezx33HBytXh&Uauy+Qpk65SLF}5;?`XVh}m`81@J2_tONkPe)Pms;g(_2eW++ zkg9hp2@}3~8ZS$IuCIk@=gg8I3X)?DkFUNx^kpL%iQlH&cduSyn(EJsaydv;oF?r2 z-v0n?YjF?3wL?m)h6)ga&V6&Jt5RW63MgjVhbKgOiZ!I@`w|GqlCDacreZ%31yp`R z2ldd;<_%K>D1!e0isguNu#ug=-#X5#Vj_X~cASDwI)O)2VTr@?xX->cHmc(-!exO8 zNSU#L+>J)kOl2x&Z@`n_VLe*Li@W4@JBh z56?(EvIYPi-r9vz`^Kf)&7VGiF+IP_P@7of_?*?P-qLU*b#Ge7QBN%h0yrJXW1N;A z+;-CqKkXlNv-EW*L0hae6}GBshpK398=e=Gjt`ay7zdmZa&=QpNjzzgySc$5-(OP2 zc$A@Ae8e{(kO0vwuB}D`u0W1yis_K8M*#l-Gau+WmTR?vYNe4p(K{^BBw-O7oNWV* zq~xdUkxq$MYmq)_KzQ-a9ep(}DPAqX!5BFi(gv|p zFcTu`L38@%8K}F`Tc#H(*u_mcKbEx0LsN=Zj<3?dG%7Kze_SUO|QQGc7Y!s#m?fu3*d6V696b{{VibP_hrJj?O=>j@Wvl?bCM1Vt8%6UDFI(G*w7t zia&e-^TOQv9BE5w4gTNZ{{RQXs`rpc{@?LCCeL^1PQJFA#fF-S?M8q#O+2xIJt5EY zj{W^deGcm%2f9A@YP}>1sZs>8kZ*DGe=B zRZ_BlGBU^k!S?4}Gh^{1r94FF_v)^du2rk1+a1b_%ktVO2;uhs0EGLFHIu9Lw0`u+ zU;FyPHDb!n@ChIHiN<~)biHRr^-BthU|10%2IWZyzuQ{6BdZqKc8&_Pj8e%n$mmA} z*kGUX)t%#w#@knMr})w4NJkuY)qQ5@-ka(R%_TIqgKmN4K`gJ?V$aNe;AdM}O-HFn zdjwLOg_Uf7(=mqmRas3lEK_-rFx?w}i4P!+FRk_Cm-vSqV4Uhj;`7s$m%Ejlr|V}n zx*3c%`ijD$Sywp81CDTiE_A(hhO*sO)Rpw_1jxTKI_)?;hzHQ=y{OzC;6{w25W*L& zxj|7dF5$Zn+2>8s^(`GgNdD@Hmw0X2{{SQJs-{*emX1O@Yh*V%r6CR&6YHs3@!v3` zPE>nLv1l%~`b#}Tiil^B5D$EPzpj_7J{$L0o#xF$HdS+xk6j9 zF;ti3#-USbJV;8LIEUo1-L^Nx5zAZf-F)WxsamIAT;tzs1zMi$idZmke zPY`EZ4(ChO^qR|j25hTSx#!;pR##Mby{L=) zzY>b4q`1&G#>W#Q!oxWq-%a*=^G6g{3MObJSqBPvjmCo3UL}%QVsHRv^%&8MKTnmT zd*0Lgyf})dZ!CoNY-?*zU~$By%j%4`a_O`9vp-!mHJhj^BWdG@%8alCK7BdRZnXWM z=&mj4j2Fk|bRFHo%gTWf;UYY6|iD$R<3dUuQ zOvqwo_U>2xHJSAaeHPze8I-EbJ{;$+e-oZMyThKR@WZGLgQ@A!Ci0`Gib&sla>U~y z)xCX3(2XizZ4z875=&_FouX%vQ=h}!{+jE5+S-PfsrnnIdgg|mR!AxuiKNftiPvx$ z`ZAtAr(AIk)gnXkp({)!-bc^(y^pa85t@UaVqLpMGNsN=cGUGYYm2F)$YynaNBu-3(_a4~O zl!xtva;0TCXf%@9=U(rxnCBY$22PrdW_uprmaQ)Z{8qmBaa$pyG1Xh-8L79_vGQga z<%g=iI1D+}T&hU`Xzj6*m_p_-<)i}#+q}DvZ*+nLx z=-%&vJzmhpr))8*9#gQl_;!tmGsB49sd9n$&sdtDO!_K)L^IgfBRCWDwQ80bwjB0fa&aG-8 zH1d#Hf{$)>zD|MaDbk{P8aAl%2bfYmOGW6z>-5lS6_x7JG(s5;;t1tP_Zs(!Vps@L zCM~)rse-!BpP;R~+PVT1ni%m4`LmZFi*_(SU30Hr-6XHM%~K#yWITcIs~YLz?6dSy zvCRQlqd<)3`GD0OTXK2oBo6sUY>%da^xZ9$7IzrtdOdcj9<-{nzyc8I(vjSvsU#e? zDo+~eUk~ka-09=;VGStx&>lXVA6;_=zOm^g+<{-7LF{#P_;=!CS*3zDW{DK!5J>~_ z_SL?jrMC6WeTjdjYbw*RTY58~XfIVXO0<>qMiz+Wa(E06f6rK7I=0tIB+|WFN9IUy zRlk&sgZIvbUAot%t~Sr!Ro4`TaRz^e+k~H&zNv1Z>Uvokp&+a^6wV7P9_oMdI!jIT z^Jm;9j;CCea->E-5jyIcUoE3$m(L(cgXyk)>YLQJTa!6&m+&Yai=wwXq|W4-8KVT1 z8ds?$rB)aLkG8io`cw>TLzn;wWUc8gM2_JoqFX!**rQsA2s;)`#rlf&|O`sevJk zmvg;IRzwg~G?4Dzt0Bw6#B;Rzq5hin9lm;oj!0G7LZo>iup}7t^!&5}XlkiyL~G>eaa!vt1h-0>b2+x?y zNm(q121VM(jCKSb{{V5G`s(irx@zCoeiBjK>8)0sx)s8if~gX#{mHU9um zZ5oCHIhqpX_W}GhQ)%p2DV1P%jeRWNi zSR%1gL|!^rWrQr-TR$)y?ZLvFdwuky&>kLik4Qmqf=-_l?^h?8D;UWsBJx4q?VPI* zu8!Mmb}ELdYAI!gLh;Ey#^z^GcDCN!jAt0myvDBP)V}UUSf?!UXIifl7Sl{hOP{7auk z?Tsqc^yOu?_LoWv1uMfMrDQS`W93Re7t5Xl4{YG;I&P}zt4-=yYQ7>hnsRr!EEvCa zBzgmat=x9hr3A1k4A%|2TcJNS3yK(8FU33LXC1Ty_f>S&V3pmx=knB_iJE5K=NxFI zvRpRhB;aJ{UgZ0_o({`>HOQ7vMnPxk%WP82SI=NFaxRc2{P=#B*53aMd8fxnX$K=d)1)p{xqhBS4Nu z2CNrp%@-1WZPxVcgX-AMq1W{pJENnFj(-+`&plO37nRuO(4A1LH)cYm zw({o!9h&=17s(11J-c9PJ6vieIWlZP*+zuMg-%H4+fq>^Vl&U*Tq{x3C!&eeXt7+4 zJgrjXor5d)))n_Ts+pOb<+Gl9=w&=^u^j!5wkndqVCuU)*^3kf^Ces2iKY)wGZ^IY z-<>q|S5#Z-dU~40Z>15K@MLg1=|)L}D~$7w0Qb~MrZMH@<%!^E#T8fG+_VWmcqv__eUk zxyCzb&bOy&Dd%Woj1U-%=Q-5dKZUhdbTtvi%AVvMUEOONnp|tDBPJo{XHSusdx5Vk zI3GKclb^nf^(CICrz?#Vsz3vReRKwCcASHqa686U9S^87y-duJC*cD+*3-LoI2yz_ zepqY9G6qzFKAN0D00(n=I?B6A=UArb+=6@SKqMUMQesuf?mO#<#HkpWd9nrC3C6Xu zXD2=NnO0l_ubuOZcGqH5e2K5-8OZ$g_a|F6j&<>)QK^>9#bjmdPPU237(SZPR~`K| z^Ee%~*qw~RbTSf~7>!E^Imf<%!8=7DZo@qDqm}9|$lKZSagp@U zV^cqxWs#W3-ShplQkqdTLk9#6KumMyF4dIx9%P8zIs$ux&Zk=x!%{0EMQI>xpZ6i+aJy6o+5pe}^iEZ(>I#YAUxcKL z5Cb%0J4Oef$vDtjSUxVKB;b(X;fH)3Gf;W9s)*@o)W=HiknXrHxaS${G(b~52SMEHH4?(xqjC?>_tAPw@{9_Y+v-#OG!!0t`s?o8o=NSf zAU9$S>RUUFF#FTC#AA#PeGs=?WuTZy zgK{60)b?SkAPLk){SamLnPLgVjvY_RJCm=JHyGS9%%!oM5$&NA*BI&`d8ylSg*<2Q z@$0DcRXIT((~>@B&!@JjVL9Sve$T-MFi$C~mP(3Hfer@P#sD7L8FZyrcAA(BfD7^O zti(nvJvlsMkbC}G&Y~mbyP~jSGDpAHLabvUrvtEpAZV+lrIbk=gg0(Ag!eh;ZPLih z-n))-uUFIekq9InMubTtsVYZpPTU+v(zSndk6j2A21wKeetyT-My*Ws6faJL{ud+d zjSFRYl&X$T_t8kDHd-+J2^$Il2cLZEwj8d}+&!eSg&oZkD3ld$Mo$M&UWlMA3Hh*a z52lY-P?+f{ZfTDytP}MIQaWR&YdW6sPimo!o=Kc9131am18f=05WCY|+2SiwTdHSP z3rfdlF5ny}_twZNXRW9-&?ecz8oYZ3uRQmgB4KxW)zH$aY!h&@QrgOglkUpOJJXwLT;t;yj$neD^dF_t=+7>H| z5R#k^T@d11N^_Er&)AIyk`nTiUw-<8%MuJFNHK#X%P=`&+#cNb(Hjp>T{?l5I%vv6 z-K1^+A5VQkqN0f~^(~O3e1!MaonX3CPe26@)DhuXJqDSsjQ1DSY7GnQAI41S+Z;VT z50@M`we5015^`B?GBv$u7m5Dhz5npbf<6R<&j9_lc zj@i=8!1FzBgeIP`SDws=O(_fHx_Ob4^Cz#|dmV3{1*L3;5~K2s{zvQd){_%clBPBX z-Mj1V<_a^&&tvL!LU#z^_i{R$_Qv$i^VBjiAHzVo-S_qyh^xJ@dk*@^*pLezb&yht z*yQR51Bu-U=5e=gvDTnBu#J6NUr0G1`s-v1wS3Qffv(OvlfsBpnK8>oT>g57V{%SL->K58&P+fYnHEkDBx_S$WTinA5TfI|^On@PAdk~0 zp65|2DG|nY<3>iu1~7#W-8~idt8hd`+}IhxCr({gT4uBK?F_Ob)KXj7B?*iIWnw=( zX&T>7$#JL1$lj;==)Ce5sApn%a(VmeT=D>vxfD3tJq`gm&H&KJaTUQA83p+K^g;5j zMoyqf6SMh>PUYuQ_?E96=gc&!RB}5FY*MpqRs^z+{`&hzAassk0VHICGBfOTl8nvr zjxpOx2(EFEW@4QmIUHw?ai~>w_X}$Q1kL3(K~gd^qOl5>&y9Nl4sVgb!g%vQu%En0tpJ=%6Q=WjZ5opv4^Pdk8!ipLrp|f#S-~S8PGA<((Ugi(g8lCY8BS9 zOPxQ%PLVQz0Qq?s9BFns8L0A5heMK3p5J4pmb#g@k?tHs6JxGAlPaQ;vI6aMj5r4| z6)T_5wzKJ^xh272pqhV*m4tA`4oQ{Cz+8IgIu{)C)zx`XQ^*MKj=+1JH*}{|TRKH* zYpIngFU~xgX|g=JRU-_`pXc-k(@nNQ;YMZIO0Lr#FV?>fY_<0L2U8lAyh{rkja3Fb zih1{O9^d95WHHZu7wIegkHY%Sq7+qfS5jRl)(UCy@{kxlNgbPvYLcv|xK&hn(@JD= z<%2u!%V#;y*F~%pliluAly5AtQ%NBBf}?PaJx(;0x-e_>*yJAI8kXRx#3j_7M^n_7 zSt}}?hHw;m0iHFo-87Ic<0~l~s}6Yf)SKT&B_%`6K~f^7a!g`&8QbZd2aHuww?D){ zM=Els^U(q>(4&ALUXY_;@j8?CPqzNfG;&SVUMN!0$$7U|86k?cJjAPjRz=ST<_C`B zR)6hDbnsWko}OF3MRYat<#wZk%BHTh+N6#kbATFcyIDtkbK8w^uS#A81$1>^4GjAZ zRyoi*ZuH!zzORc{?K#ywGfM@!G`W_8Oo?+#twZnuGHa3*}*@i zirel_Zd?#>-N-y@QERl}r7Wy%_6d#T`E{&R9B13tO#KnmG|-9YZV|~Wa<0=DYzFo} zmXq#vqN;Nua5(kUT3XpD>ICRcLuZY56*sb5(k__uIy1vAlIeb}NqSbJKfKB{Ri<6| z+{=?CGXva^HxH=O{{T%`F0~bL+N|`_PVhurLZbvPsTuYAYM=13Ww`Ynk=n7K-B}y? zsQXzMH{u6f8{-rd)?bC{O9gmIq+cRq1h^|MbCHim@2tI7s9ylZ)}YlJNc&GX?1+!z zwQW^bgLhjrtj@G`a8#lZ!tz6Xj=2iyEa;nK7~I5XKAP(8w7XNcgm=lO=4qgeuqWqM z4V>$d>AcIm%1UVYk>3$ue5^8fC(}&mSel7G3~x>E_mjX8ZmKz>kcox~{WMa+(>Ht6 zyD2G{tAYa&#=1Yi&jIYcQ*n};sA(pxs@ar=APOsbZaTGM@MfaTd0JattIJ4NbEL6t zRUq=eeRbba3JRdiYRyH4t<^4P4@uIMbn`qj1HBt-0H=*lhofC$5=H6tt(8(c3lYoa9KHAW)b0{t5W;fLBJarSPIwc8Ne6rtM>9eQ0 zV%=}NP#NhXs3{`K5wb@F9)6lP1PGCUF}RF?G;gLCb>0a~0%Ew_G3o27+`CZZ#g4k$ zPTb78hr~5(N9SBVWks#cP9s6s<4XNF8a~79MF(FhG%p z>>S{e=yb>9iuCm*qHo`mM@1!F8CplEpI}KcFjaBGeODc|=Fr@_rp9tf{b$x`vYWK& z$YF`Wo+((MwcRI%jDrZy(g$!)8mX$FWRRZtI_Pg2`m3Psl0_d%mYlsbDNya{Dhx9M zKqn_X$L4iI)yC309ymSy_4Ex3SkwaGkJfojaIJ14vzZP#2Jm^tj8j$AM80G$-zO)X z10kVo5$mbKty2XD%FDHev=H+%*hc$x+M=@FDH5TTkdms$f)9N_ddX?lT4^LDeY=70 zppe%2cqnklc)|XfnKs#+$|V?al09_5IT(Y+4rEHIunAE4+<4BT(y*DxB%e{J^(f)U zCpafrQ#px);PyHZIWV}ujnp*{6$=%8g;0(?PKnvNmM5Z0N{LQlV#-Ozmnr5~rII|7 z8w3!0XiV|R46+Vz*dv`4P6P=VG5IeBlCo8LraQRdv#C;F;zf{Evr5v(g>^jR-&KP= zrSc_gdSvQSRnM?4;@d~AsQVo9ArQTPX^qvlIPQ{cnC-w{oGv$Da7g#pp^0hQI<#|~ zow)2s&{|U)DR(F4TxYgEx+7(ZvU`MdQpny>V8N7dbpb#b5{%`$GR3oW)g9g?lB6bN zjItzyvGf4?XpN4f+H2C8=)@F}GloJpBd#%)AFn!}Z>m~)SS|GkjU1VPxEuz@3UTl6 zsMOc?rKor-sVU)VX;o%mK;DF&e!ORqp?jDdLV$~m%=+4BW3t8~rC8&w`J#Eo;~78|Wpe-A1oRHx)5{vdI{1HU8RNVj^3 zuFotqF{EZz0hJdDTekz#{`zL6I|g7bVZV-`No=xS>qPLZYa!wGkav~!#7#IX*z+r*G&IUC`UT;&~?P!Zr<_^(+rnEgX^e?YGMzG&3^tTR`yQNIDa;(CghQ}%h-lQa>Cz!%saNWTlaqFZi`c)^FG0Q%wVc|sW3J)J_HFlu1u{s7A zeZ%;XsH=l=dy+WEJ@ow=3TkbpCjS6Xw#DLdf#jcJ+QW4YM3rnWMX&UL7t>8L-6$_I1eARo)lb@h}1+gYzCf&DeU zC=8?%+uv0^NG*=!XrVaXMx;lSZ0mDH{G-robQCQq%ka9D5NzYKBt@2kcdmH)Yge9- z41<;+>Q#MAG?Yn6PJlLkX3#=G{7;`=btH4drDJ!v&5};~_@(vxBx#LRl z^%Rk#3VD2`JRd`#wVg>r12eS7Sq~@gq5zeQ0nDv>TOGQLR+Gjp4 zKZtz|(iWAWf_SQD34^zhopYssT36178bI8>#TPAofQxw?OCf@3X~|Hc>@~yjLLXA z@Yl6yNw?&azXx0C<2ZH4rbeNI?WvS`)dIcpFzfm(4#?3B&-HVz|<1}aaxeGO?2ue zZZnPm)JWtk`l_Ppb?w`6tPdrau7z!{059I?=D(kCnHEmH>Qe+Bn7@y_z13)R` z6*S^VPUXga`ZXy0}wT-OLox z?1q>H+DRm~21zpK*39#~8^v(e&2=Jrz~@RfHZSflZ&nd+P&F zBIo7p?XB=c#dlA(cPa_!D&~xZDJWWd}#LA=UdH(>uvo`f* z5>v9U$7BAwo}~JKa72Qc2jv;a#<#;oEBxea8RJr@Da@|lBYd5~9=e${fUlEIqj1Jg zwyIkYPj5IAU9LBZsbNVNw8vo{;GbOMl}Y5WerMCGvLO-v+LhR%_~V< zWWmIQoS(0*peZLNXX+mf%pq;6#WO(Bfao%ucN&ir^)t`7oboa=!TM`#^zRaqfQLie z9{Q7QqZ6>6CCC5<9>-TJnVw3hEi_%M$z2edg+z0IG_GmC{n#{G2?0CKs{UERb#Ys38K~Ww8gu8D^CZVPyoQ)mh>7KTUqhd zNOOqufIfp$F4ZFfl^Y;{T>X8tF;`eiMr&n~nwerC#}Ee` z^MRzkpm2dlW@hU$uC=J|lNqQj)prYtNk;{9k8N{*?OEaCbnVWUspvo}T``KIAItLP zPwGh5S6Jy~hV-#E2Ia>(Uh%7-X*#>ZyY)RnFPxDxz|GxU41@ZQO=SbW!T}c zI}HGA&o%+>c-8K+jiM!`(rrGY!q#e5kQoniz~e*jwbR?~4;?#&-5Tc_WVOJn5-8&X zO1)R*3%rLI+KM^Ul)?j&5$%S>TT2Bt`Sm`VO)$2uRJoR57}_c?ivll(1RB{NdWujRms z!#w17&Y;Z!Vnb(u29_40Sji~lap-ZWaM#uCC(nPe^w3hEqZy0-&QqfZyhRQVALpp@ zF9c&8YCN^o80JimsLr)~WX~CG{I5N|H9HI<*3xylAS)s*_2<*JrQ0nv_q&`{%WX5w zG|m(RT;ON%AE4BJk)pU?x{}{!qKW6IteIhoH5`^a56jQqX#(A(ar|$=2d@C zIpjnO9Hi757dZ0=$)85iRDsfU1YqYGBU3J2JsL{0(~XZFA8_sti`Tss#p&zeRaqSu z%|+u51`q4sRJ7rN0v^lNh-K)=OKX{;^GcOcah)q%0eDxC7N-`;ww?E=M4N`_}hQs_PX^RCmxA25v4``#3oIdHL!%E)R z{!#SM_L13(XFpv?l2YaOV3rz;>xLi#KKimRId0)sC+_ZTj&>c+dDay(9s>4b&ahpm z-1C#~tiu4ngaeEWj@q1H=13Shkt-p1X_pFEI^~o$GoewNe2vIx+wZ2RjXhD@R-~vL z72uDqgk5@hsNL&PgtXi8aolIHBSopR!R|3lihZnbXQ-|6O8erJm{*r<@CJ`hf1nje+XJ_L$4iQ!a2rw*vP@xSAObaYuwxAb)q;4877NUeDUPHD0Q|m; zPJWu|ZoBaE2k7fPM_JtkB+|l^={&`Ed5#B`ySuhB2ishcXjVzysIK)vxwi06I&bSg z68gi$BX5HFd8oLHYyt{)cKMY?F@f771Dt0{TcS5Gb!-6^dY5s)?-EsYmhsW|>f1$R zZ%}NRj(Oad;qphqImz%WSZTiy; zbhYv~_n}s6N%H`vd3QDZkIWF zMp-=AqeAgW&65!J`s)1a-w$n_RK|XyvQyVoQ$!;ZYz9KT#&S6r_tydVeb(1bgTFy} zw@rtI!!r6X2eJBVuKFkTkbbCHt~F?Y&UQ@_gYuklx9O$znic;5RXEIZNvo>5{Tsi8 zth`_Snd>NeO0TQB3W7-uy$rRM@tnyB3sr8>u{`g!7>mHfm- zAjc^rH@|H@S-e-Xi5^Skl)}=Vnc`pb9O%ZgyN*Nw)4KFxkNY-WHheTvFLXVQhg>Yv zQGC;7Sd)!g3cWGZ?DE*|DB0x2FAP61{(2X7>G+y3Di50snF`~&kb7rKy}ps=3|jS^ zJXh#Grv1%ZwxyeBMloa2@yF$zTs;-n5mww0FOj`fHD~Z9g-P5G^0JR`bwJ+j6?Te| zxlYr}Zan~VZYvz~Cjw-m_#GiyN?sKTIS5Z+b32kGC6SJlLp6*?7({~tU znxYyCkg5n@ozQcPeRWnhf)?r;ii>Ych^gu+NsZB$_>MjIV}tb9aM}Dr>80to>MHJZ zQ9qW1fZz_<{PoP{}eJ|GZy;IY*(bh~^{w@gr00VRSX7`)ZBrkJ_f9 zzSJl>dfv9s;e)zzUn93|P?z4Wy56eVqN=J{WRsk?In%#R&^VPnuA|6f-goK*v9AdHqhd%|XBz5Q(^AI?f12$vMUkajg@;WXW>Ck;YH<&})T6v`5TI8C$+i zovBGYOyp2Z_wQQkJpTX{Lp+en8-`a$D#}5~IU2{rj}*F_uWD&(x~k}E-UJaSbom<| z;BX1|7|wuPD`Y6e+=5O>`fEjxrMi!+W=QQe$!4Z$ge_CF`B9u_=5I_5Xdp_+w#;OC zo4-%>!Sw1RgWy21be(nP8oRX{hNh!Z%%J7F@-f^2?sUCWQkr=Lu{W5%Dz1ONtuChM zE1!o7Dla!$U}}+fib|m--!L8tU&f>k{)fJhuGY#4E>HWm;49;0Qmz!Eb{{Sew{2=s zeRr0?x#HGRzJpTYsuD8+JsRX6V6?}mOuM29$j&vou8K#9IZdp*5OmW8DoM7TDRDZJ$vF%@hx+U6mgps*X(xlrW%)}p zV<%S!1DQa?^$=QyK_`~uYkxzn(9p18`^N)}>fY!t0s7OgVTS2zwkV^Wo*1W^MOci$ zWGOzG(Cep$9Z6-m8a|-0O-7+Wk;>&2v+h2ljVb$8CCY9Y5syaFyY`$!pDjG3_<#cA zIUV!f!gm7?F=YjM(mi;*j%=Y(+S)f$i43Vbq>E9Xj)Eg9WQPk8y zSf&WXr}&_gfIgkT(*c7o5ptvvkr}_*x-OfRQC5vwj7}7g8$SO4m%fGH?iQ={&N_>I zOi@H2%FQst2{E0753d|)a_>_eO?$^$6JoK$jB-An`Yl7!DO*bzx6HH3?n(q~kO!kL zap|IN3xkOVamWyTI!{$n$vkJi2=+fsGEiGkpE{BjZ-)c$zTz7kp5N13tL#*wuz3th zswj|d<9DwkwlyM-PMC&COcyyQs-jUGipMd_pIm#6+Pm5?0F|z?PDz1hs;p|yI7k<2 zknB)naO3nkM!clL<|=8KS*2f^JbQgK-Ca8wrGUzIg(W3l!=Ft3G#-M1?lYq{$iyr+ zk};E>!`DQ1U9zi?W1mBG&GV^a7Wl05zm$eo5&Y~>4CD`9+ADVGtAV1Xk!Zm*jk+)* z&h;npbI3YgJ$HQQj-sNuSt`<<=z;}eq^ZHq2=xbDO-bW-hJGtqVM(kM_FkdcwxL5D zHb6V$U@*Xu+gMjys?*uHE>QmfNv&Oad(&ZTFqOqeM%7xndXA==qH3AI0f8rJf2MnJ z#x<_0jvA+ovzgi9+bmP#0D;&O>;9U&rRiHV9X%A6id&iU0=msgfz(N#l$9KSN3Mm# z;cOQ*oHa0%qMs-;ZSxR}hUdTAxYl_3ok_!d$A9tm5xIKA=W4q&ZBn$5N$~M2hLu_~I*ugv-S*VJq3~0wYi=TIbt@=F8`qtq1aN)0_0doE zX7|$f6j*BHiC!}tl*`$P?UC61`|Dh465tJOGyMmokjprJoqlgkPKccTTF{j2kBoX} zQc*eVa5QjpGAucgA^ds92lmrtf->>|2O3p{2!2HDT$51$i9^M3#NQxBhYz`!G`WI7tk;xI{XV+0GAAbe4pukv- zyysDz!q=xk$(ts)RZ1|^K0ULj#n|^ck}dLMEWiz9C{%tY<5k=t!A0suL4%!rCyx4* zX^?g~*TV#0U}saA8^Mvd#=XWdsS8h^Olv=f$2lLSyEBW7$eqs`@E*HPH8aU^*e-tB z$waH(H9t*8XL^zujQ7FTP^&A39*0CJ;i;%?iB3NF?V}X7cw-r#xxwRB0KnO(R2;xH zJH(-ale?YcQX$5^5t+7Un_; z_xI5M0Ns@e+$cQv(<6pC;A2<>B>s5RZ6b2&2q=2di@DV2J+$E@aKfdsH3j6S=ifSlSV>(`BY_uC zfWjuubt!?V1shxU>~opyo}Ml4}D0CbGIGFvoMJ=2*}48906~c zoM+E~!@mUfCs3wSOpYAofb>1JAVDl=<;fZR@vT=iGSjSsy0_2o^wcj020~+YJV{+Q zd=yS;YFvn)jl}lWzNfyMt#R_4ig1Q`JR2D1?v698oQJn4h;3N+{}^?c1F z?j?_NtZ;HT)?9*eHTNrw0jP|^G{Z5jJ7Zh?ui%y^2R*f>*xq9Kc+Nh$ol#E96oiZg zT?TE_Rh9b%~2bH3${-f9=g_kY-hf8G89@^ej$creh_;PeHS>5Hv^e8 zR)s~1R@%+Ol^Ht8>(vuwEFgp2lc4~y+;Nh9b)HT}-u}Gnf`~6kX3^(VPO4iB%m6RI z9Qx|nsp;r!^ppWB&2H!s<&*#ydF~h74%yXR0AQfL9OnQL-|BSfX1q@w6jW8!21vm> zP?3ZOS5_FunV6EvmvNVnBF^G5{dMXorngiqkv2geFwcES3kxx9t0?3!(Cdvr zN{y89l^FwFoOd$RZX09dHyG!oSC5jz<{p{@)RHWe4x4${Pi{W?hi|Krp9%;FP5~!a zUaI9ZZ34-F%0cIopt;DwgQ}vI3`*9eC0Al!c^%KLv z+fu!AJwmX?47xvMK8L64Ulb@QW>Co&@kk@s>-74Up<%PYB7zk-UQRKu%`e#pt8LvG z;*A}3T#AV(5gfJv$@=T{B0jsZ*T)=*JVz=q^9?7f)3Y#eIK*1BQMLDd-{L3Mbm)%n z1w?DNj32qiqV+YG&$881oDn|cBOG9iXxCMBMYaiImIb#nV4Jl*Bbq=fj z>ey#P^F-W^`U!EadWxAPCzm-RJZtO~&Joq00f#=CEvImriKi-{i55wAqjU2Taj3A> zd^s+--bMiRZ@2Nq#vlrAbQGpFpx0IPDfHb{TAf&hp#Y{BwAvuaVgc4`i?Cp*_ zol3gbwJkV;7XCDY83(Y?MYhRP^UE`mKpE~vm}dZDFQibJcJnn|F15Fs>1(TJX=af2 z#|k}w><7?mO2w0zIUd7A(#p6Bl8l{WQD~~C2N+}Rod=W5bJkC^85JVV00SPF)-%<| zOMsYX1A***x`|UIY(T52LE9i|D*VS8B)1ywJVPyexh4^)-ti>okuFIX{3qK{N6zCN zyXtHpO&qk|xXgcb{{US?Lxe0j&u(=_9OiK2iL55)+CN=yJYWv~n)DH!yYM;IeZ4#C zrgI+?Q>i0A&}gqp^u_0@ZYJMnqLQJCks~USv8m&8djXNoporM;2CWYPd`Eu^y1rkH zq)Di*QT#PpFa(7^%%|)~KSQA0Y!#DG%Xplwp!@olM~A&5(;u_O`r67UZv9hJNF63+ z1t`l7!29~~_R?=rb*0a$ZZ%bRYKNX_pC#D(`+Wztiuj4pol941i>~@g?r)Yln9DB7 zcg+C*015v98jo?$xYbD=+UE>}NlNV4>{NrDXXw>zUsA@)`I$dZORsXj?Hv#E944n! zrt@9K<~@dlMJh>9Y2_a-a;HL%P|TzxvAz`Hk+KS(u63eY%|DwQh}Z!3&YlWl15WC2 zL@$1XK?(BMa-3)1O7xNloS#BFgWE+eS9Gh7aBl044uMhCh$^3W3_ZK?tEl6c%_zYL zG9CEKTTs%x%;(UPol5B%yK2Whs^$DIHN3@>J=hKu{WYeo46+|9oy43EO+&4;RYgYC z(nPk+xsZgeI&8)*cNIQ7s=ecqxJF$oF!9TPV( zEPJM1t=MDx=vy|JFGkASyvV_RU=Bw*o-OiANV6wma7I17b)uS{qPg(|EXO(R-&5wM zxzWU95tIaSH5_LKOB0+ystj`T-|4MY_0z+;C#I08@5cvH1szmOlFJ@5+a9`?JW|gP znqX+TJ3FZ8YSA-zNlpwXLh*aDG-Fesw;w-5Zh$kH}*~C!`Rr z6;) zTaDjQgQ(MWrOKiSspksmF(tPWPEUSMI-1dl%NZ6_SOSCfmgV=U`cg|>mhsZCs$7zt z1;z;uOB@xNJ;(7Q95Rngk?Eydf&=BJq1Q3gr56-Q2P};ewwe~x-!290D5Oe zXu5_1kDZmvmu-&G(;ZtuS4(N2_n+^cO2_e zu1_bvHHbjD!0n)%DB3!X)k0JOQOs$zTWM#snukm}w*dOW4qZs?p4ieYHSV*gt`gK+XTfk#u=QPCK)cxe zLn4cpYYdq4)l`>lw6rx8ghKxS#HX8r7#=(RT3J<|in;0Pl*24X=27T<$I$4PPIZK{ zthARkX<}q88kXT7+-mFTUj>g>^wqnrs~rT5{3nJ{m8gUsIrkb)+pGF$A-ewY%Jkz< zg%EP0Ry23sr;eRwYmCyusy3*Q=V<=gGikO}-y%0dTM{H+F*CP+r#ku89Wf6@+-CjJ zG{y!v5lp$;pFnk9Uar?`bbeHBO+HE5Ju|H>6~3y6&l4Q!{Y<$*6MzVxK0&BP56lX zCpuc|DjYcxe@!uhBMk>wOO6iQy4&|hGo*lIj0_Q> zGIaHIJCMPf*no6{rjfY@Ce*HB*m{QBB)&v}_BqCVwYK4Lj?*}fUB)>VaZYy4ExC%WFi&Bk)uW;L2QZ1JEyBin&Z-w*l& zqwh8`)qQb#3r>>8t4$jK%j(6`yDkPeIn{^L9YxUBTXoZ>Y;jX`_e+W1qM{lnK6Lma zYXZl10rcmCjdSDAT(xMl^-)U`?edx&>|jR#sqAy1VwUW+PF4m-T#%{=z&-Ph-ueT5 zBwWPU=kfTSk4>vx8U?8b!I|})3)lXQmYik_+J}IKBL=O zl+u+TMd(HBy{&TXA!35zN|Hg3HLxU7PIK#>N3`4Gnz895s1b)42hjaBC9Sj7Ad-@m zV5mt*W0C})<*n3CwhN?Y?QBVsTmVC0#(VyHDC64(dE`RS+Uo7Z)K*cnl=4ThWH|+2 z&;#goE~1U6r-G&_Wed9qjJZbxxMQaKjb#kB6E5K#Q^pu=A+iV4LG4n}OK&MnSqb@! zLo0>^jx(M$40eo}QOU#&H6VtTI3T8#itadkk}`V(?X0?crf(N{9V39e*ijxA@nqv~ z>8^_e+}9QB}!1q;pC^ zk8&NXxIC^$u5sLGr$buo6?Mr=)b=*1x5Q-IQ%@0<3pnmn4B>b<_Rg(;8ais4vfWcS zs}eIfF~+0_nL7>3=-9_UO=sY39j?`9sD{1=RTGHTRKYMY;Bnjo?ln`WYjmZ_sqI$c)`;9OE0NmEP#HoRp$Yn#hMtK3fvIaTVNzLMw zD%-A-H2_`R|~VsPv7_s%;X?hdK$u`XdGz{_`4^hml_tvoeJ6VwEiK74=E-2PsgtYNlK70KbXlMPiUXycwW z$C-opbMAl3M(v(0btT$*T1cv5Rkz4eGzvpRorW{qbK6X@v|Q9yMj7TRR!G3cbNE<# zXWVK1DrzaQy;GQurL^`Uiiac2uBq&G^t5#zWo+^@F_L&nY}!vb`uge}kN17*{{T8$ z=@y=zC(4SEyK8$eK8K&Sb!XqJ=xSoFX%aJ2OBdi`Ly@0fx4x;f)KJiM9IXNdT91}O z1`7WG-|fb=HkVRF&p*0wBR5dl?DX{kVT9sG!@?A027O2VaiNtKwQ8lKr->X>joxaU z7LVWRMu~N81xwqhtL-%}B4jjsj!rR>Gw|Q%@}=&WesG<$;0RgN*u-^gh}(wfdD6UJfAD z!dMC9W_Q)Do8jc?*zZnK1&z_>QS%``ha=PZ+M0TVs|bKZtsd+jZ_w+V zdI!bt_S2>#74^YpKnVnIRV&**{k3R#PuFqvOMv2%J#B!MGeDCoE0r6|@VyvI zDfI{jen_a}(#d3a#&sV!{PmD>I46yCWUQ08e!B2r4;qi=ldP6(cORy@S8U!~fvn;z;Hd63 zq@R~|vw}j0BaDu9*p(t+?anot5gO+iCs>lK#fN=-0p)P0H8RJkx+I!**n=nNb+u_7 zTWW&LJL5(zR;ijWyK%p{`e}-S(?v|66b#rpw$b8e-K<*p5mK7TT~{=tb~%i)DrC0VjhzJ=Fx zMb6$gA{GNDIL@w0dWDun2$?62In?@_6UzqqAjm3vjS9>!a%uXOpq_c0;WRVC#bs9n zch0r>>ITwvZC<)6o~7B0TYjCixn1Gf#~G3q86RFTq2vzct$NyoQ}EYcDYlaz{EJk~6Pik7MhrV={d)2%y|R zSN$|bir8c!lY z{k0>`<7$E5^wqi(J&tFI&&a2d>#c044i}y~op`Vxg!a}2k$H2jCO{MGDPy!?ldQa@ zOcud8)`3(D9fO=}Tbc8^M(%hTsbD3-6DFFd6V!374^cFnMle3Qidc@|ep>pnk=&7y z+e6=;rNv@xk{6o|Bj-Mv^TP@NHsFs_tXiWVHh&H|(3-lsnyH06K=RlCdg|T6*IO}? z=dV~)h=?Z#wt>{iB=Q%LvUFXoLKiUBI4&?s#@WVxzWTzl*BGU0m=UmqWM?=UgaAgW z+J*&T<;C@5sg&deQr^0ZVVbUS94bgbf_(<3!%E^Xq~MMF(qh!jeXSvk*Ny>OEy;7Q+;Ahjs^_O$9PZ z>g-Q!XTG1d3a-j_x2g2iNjtfTT*G#A z%!yOd=}ScgO0jWJ#{;?J)BgGqQAbxKnfPZui-Fi_>1pH`Wo5!}KAOZLqkQOucI~MJ zfyjeRV=vvzLTXB&fA~2+F`Z`=Gw%e`vW4W5H1f+EOOU@P^**}Pe4WJz1a`)(9Q6tN zaU&RtQ97)?WTWus1M97iRLnOk9lg78_R-nx)UrRAu>*p0jy0)mlrflQ;C#)e*?Q6PAkyEBE!(+1*7$Z6J*0PO@0yp{H`(4&?furhwO8oXh+^6n+)< z)Fr;Fdq)vZOx)zCh8|Wj2L~V?KKdhHBri`2IYpI_oad4>pB>@L5aWV9a4{;msR? zQWs(rXHzZKxo4_fsq!fVpG`1YGF?_3<%JwH)YeAY(Bc-O8KLf=&^ z3-huZ55AdiU0-dYq4)~~K+5ZbCO;4Q=_WOD%_QKKW+x}tR@3i3=Hf1!t;^Ur_@1U| zCaLr42I74*!eJA}qBz%q=ry9z)77_Ax{xju9mJdfG*;6Df~ra>WQ{zvBXK+&X~j(U zDRYC@i3*aEMXFHAk_h7+wZ8iuR0@*Lk*@rJs(HqULsdr=N0^WEgYz8ZYCZ0*f>2z$ zJF&^*fOJDx+CYmdWq~m|VdiBv_w>-|Xhd>(k@1djbi}41oJZfDM!ri8Jh^S4WMhG( zikYC7?BXs{NYPIo2WA@du>m_mqX07wKKeW?K58{`@UeP-i2G=2(#aDOP=#HIBpd}j<3C*;m#r>6 zL0e67sSrgZQL2KG;2a+3Rjy8b^Q$g`$!C6tqMc{*ZZj#Alh~+cTzzwa`e|)QMZqJJ zDX0MyVp)_uZPETLUTpSj=Sjm)Qw=evmZp^8zoPIt!0s|nbE@aWeveL*yfr;6-fc6p z1Us@uKDrgtcM6W0se0N;O4N_RB6nZ5v|aA?w@Ez8x#X0L;JL}?LrPYr{oe!PR?Gcf z7-amx%{zq(9E^LNX!BSckU{(FX3U>o(^#?*+;OG@2~mM0+bV=LB1XnD>*=ER+pJU3 z2q0ip*93iZx8;%ZE=UKRd}ne-ik9tCB@IEFbooPUZN^C#pFlspj!@WTr~+9Og!lK+ zK9#Yx@}>w8G8+nU>T|2PJTj$wJT&xsUPt@Mv7hOrs=tjJgOrlij~JY&I?(qgib?jVcQxlmDft|X_ohuIWoGc zSX zpviBjP&3JifIpr!8<)h=ro>^8U^uCP?OhbXIRD6J`N^I4I3BR#&Fg6+JLMz=U^ylYZ|Ne$1ggSP@@ z402`+#GhfT8P!SMfDX6ENmEgi3@s=9BRbXa(g!IbkjijD8leZwxFSlTkgBrCz#$mM zv-6pLSi5`c3UMCCSqUTqgRBguHv}I{XeB(#{*&a(HA4jRPEJ1)YAto1st3%DhdksS z`rkX2B#+Ph>pAMW8rx^L zktA?}(Wg0FjN@FT6@s!*wKY+Bd-*H>0Bv+%fOQlUmjJb6Z8ba@Y>qSk0JgDyx>2Yc zWP=R0HFqdef_O4VR*#C2WUiwUi5Ps?@mBE}o#P zsG*KEDv}VVz6Z9DC-F{ZpUJmK;wcFQ4tt8!CPNs&1&|Qa zq3zvGXn2+iZ-$9aWl@eaW`f$4E+mMC4bKN$`A)U*FnHzTzDK5>x?ioYHn|Zdca|?u zb&ak1ZP?9cGdjg!*)5SR;cM80JV2=&dTJz=7^+|8fP;WO`e5j5g|DhY2XC2%He*&S zJu_*go+VV?rx_jbtO9JQw!T4(Q=>PH;PEMWdKKyb3rbv`G4G+5KMG)KbLQK&F$d=< zk^%GwzPi?u%hK>t26T^j#xspUxl(lCk2A*()0}aqXw+X-7Z;RUkxi~F9vEw%6xtSSg7#ODMN&JKl8{?46yak;Y7Jv$^%iE4b{QBFNQTk_Vm zUs=*w?amM8YA%+`6gqo>_=q>k%KNQ6eoLPu06egApbo?j^7qktZj_c96H^@+s09Y( ziT+?Q`1tnr))$VPn#mJXM;=lFVyN=lct-~Xxa_2l>#dgx>(o?m%SfJLA`vRWNGzms z4mF^*7se$lK%y>Bc#_`?);jF`(g#^ob{X&fy7^l~ZVM6k#0W~PZ2pbwpDpc%b^W*h0Iijxmc4+gw#KxQQ^_aK9ErjF(?QgeFH7GH4zcfK5tT`gu0JhQmd>iV#X<1X-Bx&BW-J(DMfwx%?V#O9QuSsk zi05oT*hr*oumtxF?cZ72f2A#KS&n9f{{T}gn^0mJy!9pG`!!^9-6m++7i?wN#;xiA z^aq_5@bkp3q3Jd+>nN&vX5O{&vay;G4T-7XaIQCC0@nYcdYIOo(b z=Q#9n_)Yi&+%1@5By-hd_vp~{qBw*l%^!L%2 zx`ApD>1}aT9pvp!P6xjqPiUok=9SQ&;7;Y+V`uXt{WP2+z0Gl2diHgP7*=3&w2$kfHyG#)q6Ar>rXdD*mI%On z+>b-w8ew)X3j)mKd!fo+pZ)b)O7@z{N_vKD1(Ae? zQ1eTGHg5bXdmpy2S|5b0nu4|;En*<1PY0g*AS+nnRhg|InCsCIK|Bhzbcs_)jr?@o z{{RdhVc+a@Ev{49`l6xj8KxcVDOaYjt-?*4#RN ztE!@^X(KJnZZ^A&<0I3J1y}-09t1wv6qF1qAbm@!?ps+u?oDq;j-=VqyP(!()HkCqf1oy2+_V`|WZXZf1C zM*)_-6jeSZes%UGTnbpx9U67NI-s98_d4wT|CVEmZFel~B z$Nn+=wW=?~KHB!ZuVkJl&PP%}^!w{6e*o9D?K+=qSim=5!})1>{{SAnuV@}j+0%WK z=b_iZ*z4N%rZRj_zJpl~$6nX9!bgc9{{X`p`ac2Jwe46z%m@54skX29!|XNfdtE5~ z80kU(00@2bL4VBa+V-rI&fWLShvNM7LYuh!we5RF9Yt)79XbC1#Yg`DP}PrD{{X=+ z{Rdvxw63G>5^CtgEBE970MY(>U8(;7;m_P_+V+Ry5K4=xzxe0>0I2@}eJ?L= zoN7GZi}KgC?NrOG#t+0bwtx7e?XPRv3;CCtWc)hB_Zs%Ss;nZ&^e6!T$j2b?tjoZ`>wbpL>3?bbs&$qBn8>0G^um zy{Hjo?tNb0m-*{NzleSR0MB06wODU6ALeasru<{=uWQ=te%aII!~PljX-2dDBYlp& zuWFiKv_^E3K>S+!C*s$&?H!}WOQZZlPMr(?0K`{+&_BMtuW4((!xYs0hGl*K0E(aW z9X)tu{{YNj`;B{E(>fmJN7O&iodaV30OEfktNW+_0QeeDu>RWiy`_Gs;$P^$ah$39 zdjA0OTR;B*rMLe8k*+kQ{#|=s)g4`ZOjB1Bzh+?@%N$$AbTFa8sm=HZOUNX$L8l0r zcC14q2Au1Ji3nSWA9D$$y;B??QB!KEWHNam4xEe?f{F#ZxPQKQ7V)Q zZD0HP?%jF&<0bFilbhV%@BGer@8#SNB}2aBKYkn~nj|r>v7eYv`QkQy5F7!;xyCLW zvTTs}yEv!a)>MSKRHztZ4{i;bY{!g zbZvuv;x3c{%Rirt{;S0LeJ(3W&A8C5xU*FLX~6-$d&sJ|4VyVqw3H>%K4*k;oK>|f8 zk?nBED0UyB=pG{&a%`UYZ}`ESG^jnSf}WfpsZZ_zi!5G<^cq?{6gfg`+F^t8qYxo= z!xViq{su$dr|3^Xq&Emde$cip6}iJG3K4;R{Esq=bE&s9|XByFG?b1E;)d zDesvL0+;F4ue7x7#J|@sHPur@%9ANN4LGHIXB=rhYwIIrX7Mvvwg`Pchi{qV+chgHLH3bhYv=dr)(WHk-b`0R6$Wfh=<;TQOm#lHb9b7BFe zM%Kd)JtjA1zam&~POK7n~#6V4U|9$h?FGr_}?bDap=Gg6++KmrZY31 zyZAEp$1|QVS4Stva0hmP)8ZggFD%EGy;`4z-7ct-RW^G{DcT{^lYTHIS+$ErtrXd9 z0hPbl(nnF?N)^#SgLB5KRaQBpWPNNz=4o(WQUkek*boA5f(eRz>v*6s(*pJM^@#<9 ztxbWh*T8F#T>eOj8Ml#BQ${}KJt5{W!E=$&zCbpgNd?$ysTW2NmltcC7m$P|JxNLa zr|$nHQeqM^asUwpCDkq4m;vxV!NjB_6F5{zk-_I#wc0g%l~9vumQC^F{Y74RChtLw)I|7sh#8Z*dHQZe!3lziwHn`EARi2 zNKA680vS04}Ag_Ott0UHF z$O>r*;_#A?c*RCzyMG?L65_le6#@*chgq9c4e>d+m7Z-FbhK;UT`q_-6be`muLGm! zs!DGFm(NL?OUfZgqvsbaKDmeKBZS#!4;@My^$h(r`0Nuy(=uw)-sw6ibNc2TkjW0H z3wb3AkG{&xO5)K`^Q>vU0jL>2M5(R~C~KU&%74JJ9F2P>b9{`OpL;Op)Qo=S> z5DcPPbvN2)`PDdh9!KrD>*$dQ9^UQrS4|UrD2!^65bcK$sdZ^mMcflIlF_-#E~zB# z#Bu|0iH-YIxDP zvYLnTvoK3=z59Q-r8uywOp*=lWyMw46U|*rccw?1FY6M4FFgBQLu=@NW@>qWm{sY$mxTwrEjsMsT+uLPxrM}K(SuZ;9vNjbgVA5P!PAv2 zu+c4>l5k3jcJ#O%JS(YxUBz!={-2(-`O4Xq$hQ2IaKOZ!qP?DFSLD3d9kCmLL2yaJ z>2Lfa+%fl1fyc$5^IH#gcvtj2>HNX3PMcRorSj#$ijO~}kB6tf_f?;#P3Zk?Cc!j2 z1Wl@E!{D1ENQJ4d|69jzNd>_h{d*}}6uHJ9cD10(fO4;v%kG=sjn(fASkX6=|Dae_ z{xH{UL0IEX@ptnXo{{uNGWU3nCu?OdlpZr3&pA@-e2pd$k#>6U%&K9{Q%iAw(51#h zjR(GhXGs-3h5@^f41sC2V}Xi^D}@92SH>yj-$QAwD`g%EK)$fDOc^ztm8=0(wg}-T z;50Vh1rd{&y`;!Rpf>h=l;{NiHTRv2|3Of1X(-;qKk=)>O_#Bz0pFZHezanXLNZH4vqCZUt#vANM9qE>mVd;_dSCriB<< z%?AeykCM4p(E14M(bh}z%RF6^u~FY9lvVIUIgOSpEdc8p#a&L9iZ*Yx8lPH|{K7%X z`#HCzcUX-Yl>}z1bv~0+VmQuwuKs>*E@!eoLsm@FSmWhYF(71#@;t0v`)484vBZnv z@xIZ271?$%n|q%cc*UH#Xd^~8OuB_%nBiP9#!({A+rl%uc!N`9Os7wVcKN7)FKBf% z{=6lgy(3BOaiEHqxA~*z3G@*j zmd2_M@DnPMcp`>0TZ0)3K$WjY*36yZuXk?%(abI5Ae6n6F5{koM5V@JN7>VV{jji2 zSw0@38$f0?pB!#iW1fQ3KffAu2ex3Eh^;UNJx$sj5(h&;rKsFaA^)>cf|e~0`Q99y z(&Ug2SPE5x;0 zLSi)dmkn)XVQg0FgfW3df*|v5SUfQGX=Ol5>JsSa2nL*+n@elNBnO3Ji_By8o<1n1 zH1g*?M0x0ibP%M|*i|tqA!Z{=KoGky zA5Un*MZdmgG>}%;>7~beh{?&0=*eC%1qV5S*iWQ?IHVk15F?Vh-l-xu{?>z-MEzl8Es5|<>IlBftA9qC+O~;KuvQJ3vkY{K`RSAU%4mBxO~fnyw@l;#=vKscE?nme2|oVv{`bvl8rXuNpj}5JCTScfeA9loEjha2M+ZGM%MsV1j1G@NGf7u zcZK6JlNFG3;o1t-b#8_2^MPnTKxmqT1>Y>kg{Z}QuF-Dno8T_yGmbQml%EV{|Bc}2 zPz3!F#x32zwDJt?pm)vK0Eot=P?n@Z3}A(!;S|slaxpgiUpya(FQ@QVcVAUZtbI(6jWJC4m2(N>k(|r`P($z*EF!%)Pa5 z1P(_MK^~3=s<~hNi#Zy{cZ>_RVoBE|ibY459LYpY3)#|Nf-m|d#XP?hxP#%@HVqLI zCDxXO)Sjy);_RG7DkDwfIdVgeyA^F9vb{)Lg{n)4fAxIL+RS7Iuc#xRtDb-AVGgy< zK=o(f=eIAb!(Zy|Jxqdo*5U-eP&xA1&Apknrqau%_%a?=?RscBF8eB2uaT>H(j3$v z7#-A0oElIiaTQ0$U)q?u+`w;gCCd65+JgMmz;|DS6XzPd>6yORsomuH{c%&}B9WV$ zlsC!7s+$k zFfS&O(N)b=HT-A3Te^UwUuQ6M(lKKdQR5D0Wfa7qNje$7!0q*YKt&TjxiPb8{$ zWydqP9xr+E!7>hxct4LS<{l4i{{77KU2w1Q7xOWz^;}&%T?nOVpkdBPj13{ZAz!Jj z2xV+S$X~ zJVr5`NjRWeVX7M+f66JhZ#L&2op88+ddSS|gSiUx_UMRm1Epa7WXHbY5OTi~0b2d4CRIoy1tU0V9 za*(BahJ+;##y}A(X7O>t9fltKQ!aDKD{_&knAEtV9!Hlgj+hT1Ykr6{5k`$r$~kWR za=hQCc@LWGs0#}?rACaWX{>Q^Sa)TwBI>o1II?&EidcYW&_?qy%iKx2pN*iSX2t>y zZ(+q2Vg0#zh44-fIJ@YX`f7={a`pWzcay2y#YU^`tx`4yYfJ!FZa7~Xet{CE7nd?M{skQ1^`Oi-c!sB*j2&uq)L->|j< z|LVv0gpVmLlqw}x-+70Ci9MByb78dSuw{#9>xyRWgZv_)d*>UwM&gi86Y0FM=6vt> z#dBz5C9T$`6`jF6kugiW86G>@HNSksTb-k28^52L zR}-XtXj0Ztu44b?aXbQoh3xnCW!Ovca>u(qyE2uMuT!3vktuthO6Jl@I9ybSH>9j; zLg%YvMNJri5N0dJlFf9#+PMYGdFzM42c;*=xD34tVjBix_SAJI1jI0a#L~7-sM`)! z;qhz(Bvy6aGQ@M;L+IHZMLCML2Fz!0Q2E)S=$P9AEu}h;z`k&~_7bZF!j{~XJTBaa z`(fM6Vl&d1{i%-x&e_8nA;e-Kny8U8nO^s|eZlm@{#YgF_WBbSuoHSZ3*FTxl0jvx zDs(^^^uxiu`?I<6rsBG`T_ZF(M%XoYMtqtm;9viF*(KrF22NW4~@$Cl~ zQ7U;zoA+^1HCgv{W#itlQj@ZC#)Fauxg={8Kp^NHZ;ER!YiCe+Q=s5=U7(dXByT&zNc}> zUmoI%mW{$eQ9nw6rw|I^*0`e&^+7GR2qa+2(!8X$UUr3P_a8bPbNhGbc@QWXh}9H} ze}1X9x-Ob>!JzJ*LOwN6s~yG9G`ZgZ2n2-Dy(-qNP8T1#0@!r;cS#z%IO*yy9Csry zY%37K@nYHCyO;S!6HotT)5~6OOk$%XcfiIJ%`nYd@BJth4u0sO?EFol6qSw_d-Vhj zbDbSMecjA&P~o0(q-DRUyEunK{@y`r-T(&CE_3b})3vA6&u;xn{gVaNv<<^Ki7Q>t zG^Qv}H++<{tcFYDNwV*~(0``O z(i^Wgxm=Cj+pdWsf}+fVbh0m^$`tUaJ^?HDJ%jt9Na(pM4PPL~7;EtL3pi`_mcizU zA4gOT!-Q+|fBLoqOyTsd#9u`>T4QF;o;|aCf@;IFu`N1ysJyMoXMB3b_Rn1MQ?t%h z)wvQ-c=tR~e3n{4_2~SbDvshGPTXFT$YS0u$LY~+sYqkPt|VJ3NcB&Q-sNS& zqmqf+V2$+l6k9sC?SupvBxr(n6!Q_`ilMXp9605-oLBqO-oDCv#8fUjSNT7%qa+j4 z+`v;RW972gl&+|*QMuN!iNN`?%F(@CvNt!!NUfS^{;dWxMPmP6!4T)itu$#V;r46lE`( zzQ%pJ0YL4Q?FZFN@+JxJcfo+XU!l75Q?5@Y{ZPEiti%trPmgQfOtcnreAiJ2Y%#$# zqS}7;Zd=aMT1l1u=T_(#I?n*zlYS6%2)B~25$GA3>uUCE3eO!4NUFU=Bq08c?-FPC z%FgI>R=QX>&hRV^wTk*cjo#t3v@01wPsFbxKcT;RmSU12eD^xY9qZ0@pcQOkZ${E33C0NEIs5MU zOvwHJ6h%Dja(9ET@!tZ8ihdQ1x-WmCI!%IYx9e2mor?=8pGG&5CfIMoa?pFvm(N`y zrX;gH7rG?nh4(Hl%#w}tgacuia1noq44ieJ73fb1?na)2SSysw5x3_5}Ka`QCsrG6NzNce}< z9#t>BL{!jI4utVUVc9;oj?czj-#mZprwF00D^~F9w#CDrJ51HxupYJAeZ1zD+%oX} zA+g<$zA{i-WQHXRYC+|WNGZaG{hlz3@a#!KBKfi~X~)P8;@ts)$M_)XC+*>4Qy?8x zvg)aSY+2C#x}Yr13$|r4)hD?fKg=b&-81#0=I>P~>Dg>)rG)3suOzwtAO+MZrKY6{ z3ID6~Z~UV96=c5G4=VWpl#`~6OFdk#JjZ6+onok%q?k!g_pNxYzwu{K4gWekHNI@bIvq^ksIN`^Ekv*Csf88!F9GGAy{kA~Cg4-Tuw7L~&eV4eUW7+Ph#;d-Cv*b;{&? z%cO#8YEM~5rc#=~l;2!BDc}c3RBb5Bv!q96o3F~!y7GDK>s!q$a_Z?dx!3cM%GIs& zorij(K6`w$83M&-Nh_(_96fsIHXCmTc0=z3qBhG1rrC61c2=hS8m6LT$Rkg9Ikm6vnNa~V$-A!JhxJT^+&tS!XT7B00M8r1-LnJZPzC3Ro?{@Yg{GyM z_8Vy!89p=dyJtyn0c-DvJ$QRuTW18BXd=bs)V9?Y+1B~%ua|Pu4NSj54%E>FClo); zAU==FSC!3w!p&kE9eP6KJdhu|(M#P$z2c0<1!XL*A8NRpsNtuLOJJysOaz<*bvH@& zi9vA{FsXGG6Z$Hp(C5zaTvb!^7e5J1crquUz^-RhaAp&}tK_}CMKif)>F5|;=vH2~ zMmlPTbIje9JXw6!B32?*NzZH4GO>VnI>x-)>r_Vu_bZ<#4OiXGvWowV3eiL1@c~$3 zB${kWytlk}!}R4OavtwKapz7YU4Z&3d%^j;x18A@56!Q|Dj@I|kJquup^htWj53^C z?`|mu3^PUgwU~va2M#XZ0M5e9gE^a=Cil^DF+_$y67X?Ue8nHO8h+7**W+$^gozYnBAV>K&n))S3cIXxlcq?zZq0}hn5wSjG~0SVwDmq5l^=C zkXz&DH;CZw;8R!q%n?@)+voY03y(EcO8?;g=%E15%Qp;-#^xnyk$Cq9U%!oE8Y|y6`=lthd&rkM9>V=A&8(3Vl_~j*3I3nI+rYOF6uB=-M-;BB2!UGJ+d; zit25NXT2Ewrp@r-bgA1AsjQ3D(N2;DLeI_o^q)mIOz3*TzIFND0Nz(zJpwxtO<{1zI_khK z+TX6rZqZfd-bz8aI=o&zidT_*l~whb_uj0+hDVWXb%lw~A8IZ-+h5uE`V7)7mJH&w zl1?}=Q%dj5KOE+zo<%4?Mqe&in}Rm_z36Ef5(1CQI;u{SDF9$C{`4Kbs`0d| z>()@6s5Yo_|C2c`Ht~LDOT$;K2eTDY*(N`LG6>&>x~H?AOv9loIgX;Dz^k#y z5QiL;WHYyhkh1I{d%Q$6*j_1aAL0lW9HgRRG3C5%**cOmL8#G2F z0y$PqF+jy|kt8npm30b60c;aRP&NIJF~lGnCJ#L@wKN??z)FrL^ah}RQoW-Noi9|7 zv9L*~B=#+VzOTOX{Rip$ctRZ2!4jPe3Kk8p>v1|0*3`~>b-S?hL$Ot)|K@K1c!nY- zrPR6UEc3tB1+E(y11Z^N_1O}Tlqmbs5J;2t6e=hb1plESEiCf{^MZw(g4CpSE8HOJ z^Sh3B`ELTd`ua!{$pyf@)Dcktl5PRb=;#iWcF~%*yZ7o^r0)Ho%z8fk7f8gDThW07 zJung+Z!iH1`n5|zOC)+C5_b<0=?p~pMqGuQ4PjxKmzxIFqDpl0cFeyLCG@FkMaHui z*P$}oEZ8N@gvX)Y+Eh!KD+aRlz+|@CsmdvJ~lQ1^I z+NMNoM}KTU(bC0q+Y+e|ufO?*Cjvo;js{j55r;M7D#U?cC%i=_CBcyLR3OiztV6E= z4c&tb0UMZdw@~&>>h38|vv=ti9jG@Q-OIi|?`afbZP+uB$w@ggKIP%0gf)InQ5z2G z2}Ph9FNTk;G|8U>!b3~nH^ZJ%iPr|0!}5ffRAOFcR|%-chY%4x*MmG6?jI@1bMVb? zv1)lW#KR$A8PIJLTs||pjMkWu&rD7WXO@?f87pq2hXu!fPdp0TcZo^)`B2BFX+F1 ztyXXS?i}FQ0x^MvPxu4QKO|mHmXR-VlXd@fq-vYwLiKPs>QZT>szyzhdi&12`kh$a zn5K)>_+>0Ob51>#oqpo`O)fo8bSjDUiQuf@$Ku_c!FZkNk}VVfhSbPiRJ4=btmvbzoD0qkEQ*YH z-C0lJcz#l$H#0uenf{a7)AzsJlmk|#L#0V`tgG-@lgW%SSt6Gw?MnI*7W^`_#Ae#x z5EvswB{-d6P^VEsLhz9urEoo1}-^ZFROz#*3WPq>eol*huvsts)0 z*a8K_oI@P2RBgSEKYC583xTG#CLP~BiVg~PXJZ%)2U9^UJ~ATNPPVBeUBbP<9pxKV zKP!w)U>=kTvTuBfW_f7zNER`UhK5EMaf2fcQ`a&|jhA*FYvy1)%2J`I{X(5IcTHtWIpBqY9ZTnR}w|Dw--ioY_wb$eM_+7NiD4jb2yNgPq*uP zOWO??M_UG!7)qi(I-x>j;7iF^!lzeedlHtu+=tVNczO=1r+>9j2%vwz zZ(wBI4$+Lq_&dnI=`SrysDjEFtTa*S@mQBMfg}|+&B<9 zHP4dRK}+vsXR|db69W&vd*ycPVoU5X;(T;cQ?0P zR}#aiqoWTZSc$wW7sY<|#TCVV!{sgihgM-L2I;uq5rWSv({cA1!jTiCX! z@R&a4!OcjzvQz&vSb?8+>ifTRBm^davjiRIVLtZ>H7( zh@k**P}#bHm+n~Qwj!q5NA5nNiJC)T8OBekt`zM-q=yLN$Zj3Qi-kO#n{dcBUz}ZT zbN@;OcH~PF5P-td-roQkbOXmfF7oJmxxN)_O8n)Wj>8e^sy?r$T|gFH2eKfrk>n2% z?Pz|KdC6|4&53cj+naXw8Ka-ufs&ubesT1$q-cP>&x(cNNR@i3Ga6!O?^^+oIB;+oAnjx$+ieJ}I! z#&=PH-L%qrZ1Mjz;n9(>d9YfLuTffTrc|%}1TW}H38HiZcypyurph^U{k;T5m{-KA z1o=*g7|z~_$_d9{e_xroZzkq%UJ(z4X!(4gIga~_`s7f;-NPhbLy^S~;;b`kS1Yc0 zXr=O2;+ak8k3z#1*W~Cn*A>ml*AS8$Kq-MKghgZM$?hENzEz7Tx=n>&RbV`+?IgB? zd${Z0UWn5tXj9XwrQ1^7y!BONAHngY@uWs=Er1f#4PIO8uKrG@K@|=lQcAj)RqNzO zOGC1JmnnU^^hwee9Q&^#dxh0?J5$?TqXo1k3$G6k+my%+fU^8Y=ISaY#J;ZD$zlh& zXf!)o`r)3bZ#4ttR|-~ak`l=#+U(U~e zwOS*{dRf#?s@0=z05;s^Axu;I2US(r`<^-fYS_#k6y}zkV>H7!URiV|t;!SQ?S>O} zCwxUc#*3_xX_`#(;W=`x+~%N%;O#nnIh*tn%kP02qkFGsr4Du5CB>Y+3R-=k z3!6IqBJ_bhAZT8DPXPpTFR$=g?59|-6ojFBYBE3bYe4bz?7*{xduRKiXC7v4b8|sg zo>#ap3}uTPUhbrO4!GV)6;eP_mT1S&O6brEcdQ48ZIg2<5yKWHF@P-K2C(Cd5Ns6D zF3q_C2*gaa);Z(e4prk-2@_7^v4?l9o<64oo|iCf^l=z`w>7vM7)d$ndq^uyK~3f1 zuKFY&r*)})`-)GzYcLjc%7+%NK=vCzNe9em%J9*DChd^FAMGJ^0mKrQ7S?%MaWosN zX7QUZ5+|$rqoh6E4j>g<3!0G^10H=Q9x!0S^sbMYIkxehS_SZ zN>t#`9Le>xSy$N#Dm$)wHM|NWo}gn2Ah#82vU^wJ^=|;z4=z3CDV&(0Yn}^#YcD4u z%JhaoTSAn3T!Zg?h#CS|B zsB5A2#3kP(m03j)t;fZkB}6_V)wx?L+yT#_QkB85g+PT5;>A4<@`(>5zUuANJGeeV zdq&5?xbzwOlK!hQ>u#03D0vhE>Z1ZgE#{9`n7ft!EWBimy5;_?$o^8~Z0z!YO}>x} zdX~6|dpPJkAAuf1+j5%*-`^v{AZT-_E<^Kw6-XYvB**;c0%KkmB#&~lpxbi#my!lE zd_kLXMfL<=mMoDCXI=9VY#E$=-8m=-cm## z!o%!J^6$oS|r98n|RkB+q{#4k0>v2Lyq?9TnV`S;{ z_Sc+tWT6yk*LCi;VzBfCscM0I>S9u^4td`sE8ea^q=YKR2uA`df8prvt(ss&z`rtH z9XKi1$mBWR{F44xVou9}h#$`AQD?;?3R@FIM>A=?dgzh`}pfn0`k`Y-CCUHS;(|MU}e{&;hGG# z7Yj-(DQc1eMSJ3-=fpVG&*t-RAIQcU^mx{|^`nWGKWF&w&lA%p+kkLtb!M#26T^H7 zu%mVf^ANT8NqGqmbk(;SS}CPul#F7LxXTYyPXMW#wVUtE&%Rz_qOg6S_M!mZ2{&y> z5X`umQIg>?_;Md--S_(WS%$j|nj%by=>f4!^0CG!u6C_)Kq)n1nldxvW=U0 zk0vUcOlHHEux&6D&T}N0p8Cnn7$}ZgYb8Zzr*{~={y?X)fBEzGp?(ilFq)Ep!)lG^%wPlv7sPeoy4= z@$svi?*-+hW1hla`$1p_*3ZiY%kLWv8e5Lk|La+xBKiz6`LurGK95h)Mg%^!;qjWt z;UNnN+j;y$wwS?e_9mBBKLBao?73O3;V9W)!b8_IJ}_we4fla;kLr0@lijd^983Df zw}I)G31&tGJlM@1@x8?<$;k<)2DGzJuL496a3aZ=tk%w5>BBj#qP%dPg(m*j-}JKo zV2<)c%_;x<3%AwGJye&*>|g*h+S^;x4L^ z!qPRW|0C4Uz8Cn{A6$$yEwqR=O-=pyiU_E|yqT3UzQEzrr#T>(N+tw(G#sR3LHNhC z#p%HP$IF{RgVg?^s>FhqslVK>lT+r?(4AEsn$6iZqwKAQ7I8W`92f>;uIr@x8@i8| zTnj&QG`}0umd?aRh&RHjbp3-&AX|ckhL;!?MY~w8g2rk;TDY(`pkZ)}=-wRruN4Jw zy}t)8L)=HWZ(%n8`nQkcrteC8+{%&v!L+v)3I7;oXfuP@YAbpu{Zj+t@5;BLi}%=% z%wld@c(HH%ptJgYgUg)4r*FUwo;s_x6>QdrTG?EU#>v;yi?5}AgdSSuh(2|H79HF> zEhQmYY5evEz)l=azo4!J*QV>8&S~;kpXUdpVFOZS1g{O`>tHdmnaY)Ux$){gw{TV~ zsppfDHLZySj~2u0bu`Rh_`3iLMQuB}2i2!ZnZUk#aeb8SBNwiXeHzJ zM$4<*$E&s&NG587hrLdI>Ne1j@GmawrSCRctvkGYzEIg@`lK=TS|;AaO zYqzjvLH1hrO16;4kG@D_DQw>3u;Ok_*1xosr(Q4ENTBd=!#~VD2394c)nAAniec*~ zkUr&+c$^qtK+?yP(hs}rPG`iMaNm^Wh&~Et?)B`itv+rI6GS4QF02UcH)ZCbM{X^5 zog=8#rHyKL#!WZUwR8N%kF1lOk91S<^z{7?5<$I@AYtmbO9zb zO)$(JHgTd8Bgd4ML4RG8+J9!t`f|+sE4~65v-x+YI@aSfeEIQnJ|2WW3BjK{DO%`D z)t$Nqs@wGPgMdCqyuW_o6G;)T1CsZr@rnaGpK;>l&Hj0$x2s-!+gF3bO0b>uC=pnF z1#tcKSJK#%&^4L>JJ|`ZmMLM9&-DI9*vr4P0U)_nCM?kPsj8QhfEvGAT+uEEY@)?A z?Z*Ii{yc?{jSh36aQKUgRajPu<0%`@3+XY3F^oBa)u|YPiL3dhPoLdSIqjxwCR^oy z-I+G=mWq8K!83$36tOk>I<>AK4M+)~1O#@|+tAFs{rnWHuFuEweLA;U)E$t*`$O_YLl#_L45@V8#X!l5Y_(x25I$esu8 zD#5T6_T~4~KvN65o=eF*A{Vn`j3BRpeQ`b0lr+`Aol>)@=%9QKWR5Hb5z8eAKil ze*`bLg2@C6J*KS0>y+$h#>4%NAH@ti{jih;haZ=<+no#ls$V+vTkOdoup{Cle&z#s81?$~|*jn9#(I6q10Iw3-ho z6==u-cBGCxj5qnllc^FzbDQnxH6LBTFeEF)6RqN?O`lUfO56Yamlvtz-;w!1SaztB zVa4+Wy&#_C@~w%TN_S^MIs=9|5>J^lTQJ>FZOz%Fp2|dQ6Dn&kysA)UlZ-_hpB}Sv zdvE;vPJa-(@`k4~RiToXN8$1GRhzI&wdYHv1Y@^nq-RVJsm#(EYsV(mFT_2-6}5KC zx09?AWtL=)N*PJPVXzVLpnQ^*+Pd*gzaLF}z05ft!tD%itE)6nm4p`>oNkNz zraN#t$2pCPU-O_U+3JuAW!ZP`PKG}C?uA^hu$Nj#A5tdiS={G1VT0!zCa1~$&Opkj7SLu_Xq=p zl7<=tYvmWcPP zu|AExuYZ8lHL<*jj$J#D=+r#hc77BW2nt~DJFfg)kRhPw=#HgK-G-n5T5uq*YPFg+1+!5>s= z5tUU4im3%gNrlcavd8`h2|6#(#!K_uhW6A9uW+~Xd!M$Tb6bppgic$*B@JZ>)otn5f9J?KPFTBUj9<@ z^WY{Jaxp-xxrOB3ZUZkS}q8(L~bNK*n+W2jWViq^`aqVuI?j|&%k9u$&ZIZ-VU5M}uwhg-m20;N{&tsTy1W$kd| zuM5_l!@@HU z>Vb&}fX9xi4|@pO9*6V$n*lG7d-wG_#r5xjq$U>Q#_j>e_|T_M>+3`CByJAb*+nIu znT|PYFmod1jw%Ysr?MMB{xc_zFD2PCi266P&y_Hyt;7RA0;0ohn+B@77f`5am)-mj zn8TM@E%1Z1cO9t>K_uou+&up*hULpp6_HH?aQqoOEeePUNDOV*#(zr{^`2N6bo4VG z-E&2Yk1n*Ldn4a0co=;?2oM#sI7qq8iB){q_9I%`%EGVx-hEQwZ=kT%)Vg#iQmM}Z zb>lrr>6(FU3Ay<2q%~}zA4Wg3`t!%E`nSa2y^+>W*ELXqW(#9$%P+=1J&decgLQ5I zWnI7usXrj?h(EeGHYE(?yCvfSugmcbz<6sx{qJqHUODo=@PK)5fI0QoEb>jYEu?eB zA76>B*4w_&Et;pW>e)$N;>w+BW#4*(AK(;}Q_w|odDm_*rDq@A^gik7TY@h1ER=QkvU&90c8hAJZc+W< zq2!sA$hrXxNFXi@T)%vI-u-}*fJAFKEm#yHW2sKmM8(SXvYpoE^kP7PT1sz+s`y=R z&w~~J(xzOesJ0Ja`kFlHmaOC}d9*EGGw*&?coYNkMBwg0E`|Syni`Bfb-kYQL!(_s z-)>BkuO!p*@rHCiR#4fTY4F3Vpx;PXnx1=Y{X_bIzeS|wAC>!Y_p_Ak<+u5+vFV~K z&CASJ=)o?U(<%`lK|-bMR6SIZNid%ZZo`{IK?`SHS#e4D!-MA#|RUQ`ulCim9n z3e%7t!9yb6u*vgQy5$rCGpfxh3I;B2L+OB^QkyP!a--$7ifV~s$C4_9PmtEyHkQop z2EY(q5CUhsorg#-PxRTt-6tz)(EE4hwlg>#74$(Rz%@POo8;g~v7{2oqK~!W(=_S+$r`$;ClAUHI3cw|I$*zQ8>8?IpdMPI zF_Tr-V1yl^TN^6G?_$QM{m6Q+;^tSw=JX?*802f7eKQ&(c!ZdgZI=66Dh}m#vY6zv zU%dO~8si77pyuhCYP@=@S9DPmZ`_X89;KrGzrQoaqbt`|A?bz-!WaVAgj9+BN8b0+)%sX8CQafUWVAEnb#f(WW zL!8e$zZDz*;u`>{j7)lfVqSFKgk-J{clnO!pXenn-BMH({CtK5&xMr`0bKiVqXR!p zQt8y2)xEj?ffYV$6yh>D>mSk$Hu{Wiqd+6hOh0_?ZXMx0MvBmsVSlZO-!vS;k$EXz6Q`$0 z-;(IkmH9OBD49PRdl+fTny`sGUrR0dPkxASJyuau+&wQklEiIR`E1E=HU$^RJBRQ* z&RLb+xB;YEFb^2|r@(UBGtQ(ki}!V%gNxtaRwIgI@|l>hhx7Ea1-tt-tC1Z64aG{`8m=tm~|alDQ3;H`-RA{wo<_I?pq?-%WJ#c7&cU=@BdM zScc{ztQEE~AeSUzXcw6@A%L=m&+54$tNv5}29YB7Pr|WIK4* z?m#oHK5e-PD(I!5Os8*w9D8zS6AC|tC8`+JfFIpW`T1J4Hg9`SzwxwSHW7nwHeimfHveYS$waE|q2D-XW#QGX(ADzxJ$hr0m-jM@i{) zczQxt9P^Iel{2JXhu7g1CNmT1P+lmvvO_H>z!?*XFNO35yx$-xuFR_+;9x%-18$^QUqx)gZm@e%-m#X)NpfVbi|z&uBKehH?y5uPfo z5eWe;ouj;9@*;v$DO`=R20-spC?PGWD%&7)92yv>_@+c2zz=LzgsR&xHs4?>LYr7@ zi4q13Q|z=90f;Iw1n1J6c|IDF=^{HA{rl1jg)pS|nT&x=;i^E+lLoy~nN*oOjCy|m z0OG1WRMU4hi%FP2S^9lVTW>*inJOfleJZJ{AWGZB1B}mqN_G##MsSUBvYWMtA{2g8 zKijhUL-fAAk%$ww>FDEnV0pR-Ap;=OeuFRWxvJjfaU0BWvu!)h!AC=!w<2CcS;LGzy}81yk*twu#njfWi( z+T#mFy8NJl+~&ClRN9m}uy;T*6momTc8z^0`yz~zBforC9_nV4x0FJ6832i&m2u~& zxo~Mv@{U#1jO=ifpaMojKm*hBs(Q&n(m}!$W4Za&->a1y(3F5MaoE)z$t#%v6Ob`E zuACm|@MnKZI{LJYO5M-4T0EsI2`M9TRD++wYY_FqAMl;ARilmXmuJePpKoeK!Y?#S zhQh9#7V2>UWkhXGeR~clq6n5IWu&0zbN{9r6remL|?NGX|vwf*s+~_tAhHdTJ z4y^9W=Xc*(?ZEXkH%)2E)NdDR8CTfBLQ8`x zTKC|7xac7Tc{reejr>E1ooc)0EnTl?NO-P1fXoaLU*aLg*@00 z2=yW}?OQNSvzxENmQBl|1fuX=xZT=aZSg*DU!UX6SR1%<)th^lNFFK>6}KZET%VAr zwO<>wrrqBojieOV>$yLX+keF{>WEj$2B&LHG{K&NH;mndG6PYkU-OHHl& z?pmnL6}+XT)8MuWpe3}J{0({|;62kFP4K17 z4Qt+Q+FiLyjzW<+{VULVT;V9o3G+W&(9Q{Jq`8+b@^}5Ie*y}{^r?iYYAPoZKT0Sq z0DvTg9uFVBXd5x$1u~K)qEjC#J&_<1l97Rq2OaA!OHHdeDcn^GORY5$qGUh-6CQ$z zVb>i{PlT~QaF`R`mMxN-f3*1%jFPM$a4Se!{9>S_h0KMX!_*Gh;+1#9WQ^=~*_RbA z+>Z?jRFMW%iH~X_k*BSPZf+Fwmmxtm?0_~TW6Yv?_5$~B;Ux|8CA{DE+myHSy4O>trd3qpS%7UDQFl?K1x{#LrIde=2U&+ zJt#|(w!+k<3=Sr(w92%>ZIGqi@7O>J2XP(7DCW_*ZpbB2K|Bejbkry%3){SqN{sOw z(RRr@d_|tcM1jHQ=}N~`M@jHXX=q`f;@5J&9UuVGTHH^knq!{PQ@2+QX?9}7IO1;| zYisHd6oj1Y9{#_DM%WMXlpDn_{{TC35~i6vvXlP+sD1qh)}<|UwYzqf-$I?bc|bR6 zkawz13eWO`*m81A3S`~%C9TpXdRI*U05NXW4zIvyZc^QJ!cy20Rq`I*!@W!Dn$?xt z)|UExidwU5LqSVX+(rwk92G0murMkS+p9&miyI@n+k%Ik3d4tXcJ|sL2d^~!y9>Lz zNJ2_LJjdPr=C5^B#Yt|mcFV=ObNDG`gd!HTgn_nw2XpKy$87DQ(3TGNm|)6*$p?~D zJE;CbqMCgcAr0HMkfz4uDCcVXtK8s6r3S10>MbEew1pL9$yq#-YE8<0^aLV{Gk1a1 zl!3LBE>e-nJV~UwRPm5L_b2z*RCSZvyoN-K9wJ z4^GtMN=vKWq0V{yDTJyUQExrIrinot%md{cPtu}y(JH_-7DCLy$ieoZ*lYy_5F%g` zgNl&58`I?(1M5I;^X*7E13r{-bc%Q2lwm+5tCD=OeQ37lZ0`K(Hr-?>0t^rBQWBz& zqCqDB0ZXb(v~5KxCKNHv1#qIGs8KP4IK@79 zVG2&@Nz6zw#WQKCeX`;98)?DduVGS>KvC{Tx4*Rz8wtsXpT>ntcQNQZKZoN<8;O%A*EF*VJQF;8 z={`WLWcpE6svy&c1jZsW-i9i1N>YQ7=m^C)@{|xn`FIiishKKFlmbUNpjP+~#hWe_ zB<_LSQdFQsp7h$?ND^bb`b{LMD@n-rkw@mYZl>vK|6+cr8nR-h}*kmq&u{dcJBFrW`7C>`rR+v z?Fj=APoe(+Vk&LWR1)KGVm_(#sK+G;TWY{>7#xWH(fAq)@G>d(R3CNr9Q!TT^O6vv zCopk8N`$&g=u&(IB`$v`aF~wi1bnL1tl6&Z9ugK3pb2S50Y{Nl#5!MSrA&B%Ns}M1 z%u+Ji;B;h{W}3fZ(;ACvL)`tCDo@p81Le&=_f%Y6+x>vHmMww94PidYKBk~cQ#QdtJHAOsAxj_0(G4iPvZg3&c4E@}UR*0@BOyT67NG=!)Ni!Is%VavDGoA%9 z(~g)@NFrj5NKsH!{Q-=A|rzSAt z5d{l^tmoI~S{Ca}0yxBqAdn0d?anbY;RpdiKo8QYl6(>}V5vi`O3Fd+_4TbN@(Kw6 z9^TYKzyR$;ka+&ofIu6RHjr}yqDU@A0X`nkIQq=iyh7RoY^FDT;!m69KU#akK@ljC%2f>b8zSdS4$*h2+UD#*{TRi_XOr*NTHl<(4>$;PpH8a zZZ*m&JK+snnPm%8idSww6G2Hzl2phk1Mf`Klkm2~N+39bNgRH31Bg-*5s*|!2_rv^ z1Lnd{iWzmqEgM_gv=IOfY3r7Y8gn+u`|DSTtortfWod)=pvFiLX@^4Hg0~2h^NNX5 z=%)EVcUDg4!%rknm}Y@C&mjf9faXC-KhictU$GGA z!gO3bw0S~8K~hpX4&P7(bX#j{omSjyEV979xwcDMt+e?KxCAA_4nUlqOb8w7DSK|0 zOw8JEav^$h*23ZYjYZ|6*GJMC8_P}YTGFq&gn^kn5YuQ-DM|F)vI*~!GDTFJNBP~){vOkxQ#xIa_pk26dEgz#9L7?S>+1$4I-26r z=j^uaq}geDMWR%ezy=maln_4hqA{N_0x54LprHnXS-88@h0dX>l9wM^QwabNlP73T z18Pb{b}%UB7CaW&Wv1F;)ujj^&h-ock=-d1?IfQ;LE5m!n{3-RD@re3LR9-pR^elB zw*g1HicvW3PI#*xiEX=Pu=5)hy>{!s(ghw2SHX)jpX>F+JwJ5UbELN~A#s7&raKXqkC z2hy#*vFMnucIHB9Y68{E%cLl+z?G|L8@9M)kC;FQ>MGjxtB%_=gt8n-TT4qpJcJoS z2<^Z$60}wHS#>&oj++HOf|ts?MWB81qx?mmQ2}TlFgsV6eklXcVX23mxmw9et}N#@$G_N`LZxP?B-tZsXWbnEwFn_pdZ)7wzhL zj;rwo`9XHJ_bYnTIgqql0vVakUkX>$j`g=nzOC%fmQFEC%Pt)p$}F`TT@m|$mo&|d zT`dR7Zj|6%#785>aYz~Aq3cHV71Np*!#y>pmZUAq7GHD&AO*t6p7NsS;3@~B*>o>X z(_WR`YA!8AZ}T4rDDEe;xVKNsZ5#30^48Bxw$McV{r2m|t~ z4?^5L>(`eFPQ|-za_WQ;v^t%s5$F;R<5bSIyG`p?>sEXnK?xj__Xt0obfB(hKO?ME z%D$e3E#xUvj^xcxSRqedU8%x$wPN<)LS-@iYBx`1Do*f-Uwq;y>kGJEpvY2jr@mB1 zWCD&WT)9R%u0`G5g4)((>;(XJ`cka|9$~xUJgGZK`FqrRd!B5f6(r}h)aw@LXG>C3 zwOdT%kDUu?c2Or}@|1w6F(EPBRijJPRd=GlqPrmvrdtL)tL$o!-NI1XTZJZh^f~QP zws&C7eRhtv`(rPR7j_JI4ER;_65wdJCNZ?fhJQ~<^>WMmP72q2M8rA)J^EyWNDq|8iy z=BBPXt4HfL*y*mdxi-$*C&94SLU*}rkuclims-e==1*f14My>+T(6Ih)BAqKrn0@S zzYqPN`#;}~pfr+}i*6(jHy|GM(7I~bO}eL@N0f=jIp(R1^!BTxDSCFW%3E7OE`_$R zw$_7`tqK@YPpJ0>2#URE_hDASw1AaFD8xk)R=-diaYakwJ(~-BzLd&fCq1LRRcx-* z#+KwK4oDMN#PU1&limm;T3JGWps88O3G|0uJOsUG+V)}6nTerR730x=v z+ zCHBJGAt^kLKOf%}()8P@MZLTUNKQwsbMBbbSh%+q*8wuDnG#8yAC+IaTGMu-2|||I zmAD^hn#qzpa%jf6vF&MU+<}M~J>n=$4)lwqsU*a}GxXx6I;ExxB?bvOPj2-A(}1|~ z+#}9NJ>%B1?NT)+quVb^)R0&xDgbav6um9wCE^qhpB!Wwtm>C(x47Edu^BLG#iur> zLS0Fap^+3B67U>l%`>3b+=9YVwMw91^GLlyRu+{A1a82;0(KbTCjBTa0%fm0FiH zCQo-~JG|4@+e%RYK!XFHTC8f8pe4m@%11q`t>5Tmv*v7j)dQ()e$L90kp$q5JH=|j zHM)U(951VxD!LK~fK1P6s692L{kFN z@%Yzb{*h8n&YjbZ6tGjeWC#(*;Z_zT42a}<{VETq+a3aBfeG~nN6Mz!At@m3gp9|f zad9N{>HrXh&bs>-Gvzv8miz*p}tjHthRJF=n;aKnOHErsJuf$xGkU)*x&-Bey zR|-)XBc6Xh-?e&uEl(ObnLigNX|{1sHpR8*AdpbtQn?`V3}j%@+8;z;-?RQ={o-_8 zIJfqOt{>b(gOB02g69#sLP`E<5l6KD084az!&jFLokvhv3u!Nk;AwWZZKSCvhKc@U_Rn)l(hH6`cpFj@@&QZ9Bg#bc9=?=-;kfaeTf5w8 zCumTG5!!*puFis~Cqf;#Y5QiKDgGwx?Jz*^2lvH6Ye?|lx4W@Yl&}N@?>Na-{0P$dY0HHU`l-Lgpy+fMhN_?PM;+@XER%mjmxqvNk*dRbsKH( zf0+t9Q2zj^pXF9hwHu}^G^JbKC0-FeADi#CbA#bJZ%johfgpK6uSFVO4h zw0*$t?jE+|!h7;VJuapv!F;D7pS3)TFIMd3c)Y`W2|@{*iU&n+EFq zi+0L@g5LyuxtgnJx^|0{Ni#JfyC=8)(pePvo z#F0Fc6`E3=a&F1(=Xmt%%eVS3`72t5)wohkkSS%jZ6!I|{{U*etF!+AhW@FxwY|L2 z*3p*%Dg%-aDvIJ3u8lGCa`IAISRrUB@+f1={`~vZui;bX-{O9osdW2}uE9uKcLope z2dcYm><{Hm^z7~$gprT!4w64}d%Im7Wp<|Ds4wicpF8SOK<+z!RVIa6+Ia~}u0+Cq z=+Ea;9}v>rrmbytWOg-tnOlH;@;gtbwL`SR)d>wKtJHh`_^rCQ?a0rZV_Z;5obgrS zyb89aLRAnyr8kKsqy!QP_55atLu9ASgrvy?DRM-1ftpspmEa*{4q^c39{7sME1}9M zJ_qd3S8A|+>Ihclap@lYu}`V3$p~#G6^+A_`^7V9z3YXCGyF5#1Hbg68&GMbtN>RV zQhf=kgws^T$-5YJNFhZaKpR1iY6nv)ETurfQd9NzqS;D|O4>0o9D|Aq^`*MDlOUu) zB6)+r?LjBS(Q(^+g<1tPz}QIMGEeDKwswiLwQBn01uevpXvF1Ye>#WyN9RLYSTx0& zHBC*TEzgyP_d&Q=L^xd=t z&Fa#Xc!$iCDBwUpc~VAC9ldIAS$g}V?3ZKWG@V-gm~@l5))KCB*-9Ao0)0&drrBL- zIxW9fy31a7%Qh{j>vK%amT~cZzZ4y>RN)xl##Rht^N;6ji^4(iJXv zBs3d!ZB4sqQQkbqupO%3Zqk}ZguAOzwSSPqIYzK$?kSYF6j=HCf&lDWTYi-a#xW*Pwh<&%Op1& zdja>O(u1~Ion;N|18jf?08y<$NmR+gQ;zi1tsyE+GET|UDM^5!NQi?|sA^Q&h~Sg& z9`#t3A3yg}yfJCMYNLj*Ivgyo8|vK2lCY*!$8`7>3i>lT?2w1A%Gg|Ul<2ByMJ-~pdcT33cbLR6C@np!|w^B{sx&XjVTr~$#n3Um|f zOGqF9Rk-_6NeVDy+>b2CQNtYumwaY%;N%*WhN()#WipX8zC)r zn5JA(xr3bK)2=FEDDTN4n0e3jG6A1Y|C zY{2>ascJ~w%CY!%rW{d9NC!OQ{%Y3?lnt6vp<9U{A5qODJGTQ7o;%R4Jiizv2=b6g zBegVM6D3K4IW$q__6)dGN(X|lbB+ZlIwuha@~D5cph9{i+DKDM|tD-~zVF5>PKTk>;@uH;wRRu%C zkbV5W+v`gUD}@DSL5|d$l2;qmjHHP5$TYSe=0rT(&N8;jM~!Z|O#HAav8d@qC3m)x zg(+Lc+#KiY%+=j!C{k3k{o&!L@sW}Je;SQ))d_T@OeMgX1Bvq_{&7OmY>C$>=DNyv zw67hsIF%%JisoODNPBD!69=D+NuTan3zObX|7At8Moolz3?cYscL^ z{i3NiT4(un%cyH(Y&b)yl1fC3LF_%LZByucWSSz}zj5^yG~>@60#-tajzRBDH>!gv zc3W0|n8cr*6RFyl65GHPZOTeW20nkxJq-d@;{*Vo9io{DAnpgy`u!*6Lg8pQzmihb zGz^Z-5`!*ikEHe;tGMBjPpmga}fTPC?_-9 zlbUcg0ScKM@idZ;C~zb|pT?HXe5L^&f{8{=a#e{_V3Rrede#QvK4MRyB8n`g3ZzHN zy`3#m@!@RXk3^dj1VqPQf% zII2+iDb2pfJC=+Elx|P2pzJ7O(ePA}wmW-NCW`W&O4Nm(a zpS3CSly(G&_3uDjG8{WwE$89z{Yu?Zd2i?sUvReD?A=)Go%uiBqPlz zViXUocJ-mIQ?rEbQ66Ih0;X1g+LYo_3bySNocfxIx=NIS2#HLS-!)J{?sh`mFoh(g zQknj0h`b5}2DYcnLPSVaOFrQg;iS)se5Arcc~JTAD65i5@nkLP(0}TJ zm@<%Z75!^zWu^+0?!>Jr@`WBJ)`?ZJ{B7&nTrwL;7(o93QTG0{w(Qt@a^HQlS~3FP zb=+@0p7KW&Qk7EjBfeilD@xe0Q;EEbhrH%eNJxlGj0|MK=M)9!fojLu83o2#PEZ;$ z;?I96?o4{~O+9+{yQ+}GYAjliu%Gq0{IY$$J7TPM8baA|rkZ89F30f}R<9!o?xja3 zCmyB+M=E?7a`#M%({u#sNnxjLT)4fxaUa2ucQ{EWaX0`BkvwYxLp^%iWnW@g zOqDGpnF?9uOYBw8vOvaaobB5a6}YfDqu5(orq8D>Nwiwh{m=)_770`=kLAzEKpbR` zX>AKd=x^*3+z6aN68d~)lG{#F_j^N1bIPU#^d*UkbMm|uE5%* z%$P0mt{-ON%GEorwQ`F)bbwQ_3Rm$KKhl&moujey8Z%fe>DCuFO{6rZf>^a}I8MMI z9OM9*`IAp+>LtU}7Yzqc%PV5i(Jl`nXKF*8yutw(InQxSvuv$jSY`b|)Vpb5*mmVy z?lFKn0;PFC`TA0(_PSh+gmxyw`W>hAy*WyGdzGj-TvA*Ff&@a3(4^)#sh<@pb+59c zVhf5WLd-%N9%av=AP<`9cMDy+Eo~`P)zT0`BMqiJp!!ObPs$_;gw*dFwzs)$wa)Mv zkRXOqNzb8FEl1aI=M)V&J`03gpXzeYj29Z``7Wnrquc$a?(M}{+l0K>-0QFi1gjj7 zCpZ<$6wQ^cpQQB7y~{|oTK28bWhTzg{=5E)O5Y0kh5`O+u=Rj({aF`GsC}WOExkMw z8?PK81?l7XX<1zV0H}gK>8^Ecc;2t8xaOD@_FLBzrfH*TD*pg4wOlDc2%i=0-KJVs zt#&l{mOM$LCZb!iSMWNAL)trgtsQaZh&C3jvfD*u>{IJQO3!eF+%}(5NIj~v@$XXc zwUUOM3JwLbfgg9oB`ZJtQmW}aD`M52opE}Fg4LzyaKZuc4YF4IgqY)aVI$NfK7zO( z#eG%V&azIDN?dlugqOU)gs3D;{Yd=jnBb?9F+k*LVw3r429D!M-?^~LJlog97CV9$}Lantr z;7e#T=~G%gxkDF=a6%2P#bz>8KP*wYVplJdR%63txdlom6Z+Bis#2Wrzm;Pn+9)R+ zX}zdSE1oEgGD^#`Hi-kVsY*zy=7Sqb9ecw^2#;_Nzu`t*1=+NxSN!AD@jOsg;DxO) zLbn1)AQC2NzN5V>TTM2s;Hm@|%}&OC6gho<5(U$Hp=kh(+;+$1%?5-QQdYDnNjch0 zH>mk+nn3&TIqqhkw{#13SqVwnB1zBJ>q4D&1gyXGj=5<5l;2PF0dd;|?>6g6mn{~k zl_Gl>5}AV)H+^e!(sqtG`>1(`#M@oBmfcKnCxai#aw56?lEAnHCJK|0I0SzG04lXw zbrRdOI$hHUz0=la;?>b9BygmjQlZ&HG!jlfRC*g1zB^f4UZ$(ZTq^@}-*@FkwQNzQ zIHaW^dwk08NxSL9qIK;7)n94oKgDc+_{ETzhf)BPp34~ZjwobaKJD7A5(kF9bAe2Z z_w>M&9BRbcx*#?H%tXlfqWh)#^MS`ujNoTEhfp}gc;mm5`L8Ey-;rk06EWp zN{LukTozp?Rd~FH*;K@YsE$EAes$hDCj5&6w^EX%fd+WU!Jn_^UV7=IEmy6m6P10_ z^UvuO+$>&BpQM7ED5Mf|k^uav8PjWk@Xf|Sx}D-LFI+<)t_hq_whBuRZ3K~tlg>G) z3r$4*z10=%D}W$lk_3JgEZNwnU)+?ZVB~)N=`^cq9ezyJZqohGNbad7W_gOa^v(9h zQ|5xC$8%h+?^RFvf?H0|Q@F~qIQ@H7%SP0cTS7oGt-Es~4?I%xW2JO69PV75A4Ank z&nc6V73tfwRkF-|wR;etV~XeccFR=#ps6IN;y+rubj|xI01kcYEu||o$?rv;(@Z5m zI0=9N9`zudH4qY(z>XrdXMD$py&^l4Pqlmk@;s*lwK2;2F=YIOBTPt=Kpx+nJl(Ts zZAJuv6w}xEJ4}wzObtOHxZFNd6>+~J69?2#*MwksfIQbT>gHSU)}jRWgMnA=S5#>z zP@SXH*D>n7S*7@i@h}OP&VGNsYgQRltpNI?iS@?T-BWFVnFGA{{5>k4r(FczrNF2e zkuk@$OZD!ok$yo@D%%1_yo#Z-t9Vx>OIQ-4Gx?u>)!T#mOzrbWXwkJ+gCwEgc#OnU zqIGcCGP#-H{U*6ym(+V%Dh~k?Kj*zjBdG)yoqThHF+bk0>Lb5|HycqdT|ujEx00X& zWO63CCcAu<+&Hc0Xcz$g{i<7C>QJ_PHq?IcFb+@5RRdDh1FotZ;2(d+xVd!g$s?g0 z+ffhH5BP>q0g!5}prs`#;touD{>4pd&j$*K8>VyZii2G55}+jd{Oh%+gU1RmPPT3J9M(NFsU2{OcXl?HEcN2WN(< z2?~YzxS!lp7fE*3>e}RLZf+_IDO|z_`n~@E+P-YL;UBIS)PkG}y79%;s+L&+*Gg3| zQmz3WvBhGp9e(!8`r7NoJE4E-JFo{mjS|7Owp-TW!~X!BNpVV1%A1K#^*@C-ZE8~g z0Ec0)mo}kE{{TWkpT?NFqb5myC*YdXTe8zvbE+(;f0wo%ZA9hUf*=xSfjm`y-B(O$ z>wQ-%`%2Qc6jG@wDpxbyo+^8(oh@3Yr&?{l{Nsr$1}A9#U&2j5veJ`#dY2v{n|MQU z{+w+y>rGDMx8TEyGLrXSpXk5j6}1M}Zie?EYbh6tP#$yWJ-bsHwYe@|BosFL4i61r zVCU}Qop`ru;*oK5m6nnU5}}2O&mHM{ie6e%1*zYNU^l6;$EY18N( z9ud=+g{(S*=WRpCq>oB!^z@jXiPY}(Bs7GzZrE5Q5}n5&e4u_+HGR|x`%|7H$hIyY;wfzxSaCH?s z(Y_PgIEE5$b+@p4hG*}d@BsYkrmazj3yfSm9S$A9gD^gIiz~T0HhOx;sGO%S9Dc>! zR+rj>T27Z{%kHbnP?NnX`DRDSRS~UqUa8VMTItl=wRdFbHwy5`Q>!@O_8b9OrR%HH z?8D4TT?C@sg%G4Cy20doQ3~p0YNKYiXKAUeP?p`fM%#w{A=E)$Ki4$4N#mN6H|n(i z0C>SDQj~w6_`iYa%`)k)h~Emd?xA3^-SZ8lN>b1Q5=S}C+4ZP3wWwP*maiFk1}0Rg zO1%gp1bd2({{Rg-&6@PxTcs`>DYscAiEucWJ){6cdJ*+CJ7CtSrd{1>rPi+O*mYmR zSD7Ss8&w4-KX=V2uB;WH`ASG3KRomEq7u;(LFF+M`}h1RRm%-B z`DNQhuFYqb9Z6DBeE}X|K4a@mr%t$SB>|B1bK$FYGtG0I_KEJrj5JcpDdgdw!T8`=U}Ph_Xsi|Mx4eW(>)IO%04 z2ujE*>=F-eQhg{E*i-6DNK7P|TDJYv&P__8ZQ9%2URk!b&=MJBoIyO2quxJQrdvpF z2~t5?_PcS^n!yrqv;!q@gMP8cay{ zKldM6C6Z+;a!QU!_KJhkT~o?&O5AqJiJsi?`{Je=C{v0lAd`SSqMMB+%M+1u=$Exx zyglS6B!Qo=_oLfbPSL>$GCOloExHsy22wB}V1Dyb4V8Fpc<)stxIr6_0%B@v#o(Zo4&IcUoDId?nUIn36?0 zha&<-L<>!if+Hv7X~dMl+DPnAtuomrK|BmaEiHlp00Gz$M-g)1e`x+;GvC&mUeb2$ z2m96}zyf3GS^^1BFb`uz{0O@=07#RCccxsFkO|MqoGVZpv7RVp?ml7x9jS?fT-miK z0(*(ap{AZKlz_SQr!WsB#tmqsswcfQC*X|@Z1R940l^)=wQQ&Z8{~tSGZnQU z1BD;2tu&${B+QYDHn;_=5bEF)w1J8lX^|x4PTpvUBg~Rxf;&@A36Essy;5Yl193nq z045F~P|g4ekVNK4=8RG>Kr@_82=Ll=DRUA;4%KOM$#-B+C@CQC$N(fqYK*yjprjOn zdx+%Jg~acK&L%1{?5u(mCQeMzHN~KMWNpTv>uON|;7H78`%|_W*5xG!li1W&qDx+4 z2p+WttxQ|Jv1!Sij0mMlZYGFhQ%6Qk%2ap$B4-r(U)n+AIK)+JNz{{afD)i*0z1^w zR;{8*Q3D{F6x8`eIPKw+EM2KyRVyGw4h<3FrC{zjDeuifhl_Y4BR?ZWw{n$jD^z#Q zNg})GkZII7*=6+O?kYw>98h<%o5X+!O~ZQ$^l>Ir{b@>3C&UVpu?in9;+fP)xMVVy zwm6l7g-?FQgQZ2Z4RYCe3vEJDw2>b?5%0}ZQD}?3EU|UCDYU7itdBBOg%tDnRmE-x z0#AlgrFo%80QVINEedY0SVESNr$fz=z}%4|YBc2BO|zjv8;fOW#4K`s zGw5o&t!SEsh3f8YZbyvJD<~)%UqJ61ZuS0^b+~8;Uuh^iZX9$bP6EVD4Rp&_7VVez zxI~1Iatvoa^&C=Mm%p1RPq>#FMdN5yjl#e_BXU`Ecb>re8rxdErONH1RXHBH6;WunXtcb_-9khuD+*H5KldhL zGuo%^^+%y0#+rEwG976}NdExi+J(aSV|&{7Ccg{aKapm~lb(uZi|5gdAddVhH1gO1!)Npr{-X7(N1 zrUoXg%b{DLU`XWg0-Ep>JAeRBuQdA7 zN#BzvC;C%BB%CHFxI`KZWBr-e{81QfIj4n<<&xkfJKv@Kvq>l7tkDnf0cfKr50U4``7@uX+%C zJg9c;MGEOrg_s$L^!d;tHEbF5B_NTs4&%K?IEf&SDTyX?^`qTcDf0k3ZS|op8*M22 zz<)Owp>S4TM0L*KFsa%>1P(<6v?QFSF(48%N4heoQbtFA(tx{jVtGsf-C6aaX&})X zc9fKz)69eIQ1_l7PT&kA$1^`VlX=yt21+^Zt_RMcbt{y3g3{4mBZrU@NhWeZ;(|;) z)x#q%T!Xcu93;-rC?E2hkuNxq6kx)LhW*V<3lhS^kE&fz-3lNiA3!^r8s(>6@YxDJ zVm79H#QMPRS7;gBpJXVvTAeJmLWxvwfhxv8pSn58sLenWN=5Po~ zQawHW0Okc}Pjs5@h_W{S0K!W5IJZ{`DL;8;a((6`@mEJJG}iRPrzvgQ=?#wvw%UqK z&?A=$auR>iJvsEdO;Hn2;vkkMtv1+|9nRTevPHJ69k$kz z+E}+jTfDml9#AGiL~sss!2ba3OrHI-I-8GM=`OO{PlWiBDRm415kJb31Obdp%+xXs zg{`%|n$kyrr7r2`R8H3j61R!$fRK~yke=$v*WM#KV`#Ekn7Y&*LXun%u#7^8_D^o* zL)MAfR+({)zC4S#vAELoz*DBJTih>((t=B_ssd7Zz!8t15m~0`>N={KQny;$tL<0M z1Yy_W0tc!`1Lf{18h!23^;O#RY}2tMuGbu_!ngfC;HwzVW@k3T2<9#QUUoz$1NCf#lp8fnX; z;wk_E2_Osi|Mw-!9F&wF^sBp(_UpG6JOf6-oS3Ij&prdsVyt055N+ z?3AHqoKVgODg4V076&ps7tPs{-P!(p}Lz_7do4- zY3T7Av}t;NmWGodO5e4|Q61Ly*3lox(}^-7u6-4vu->(zbbCrlt#sy`xxaCM76iF<_bsBym*WI z{dro~Q`|VEU23-;15^CUaam8$9DId(Yy3iMk7(TitJyU8F5X&rFKxsrS1crf9gg*5 z{@U}uPT3XfS~iM?Qlwb5{$lz-}%KYjg-2r+l z`&{xW<*`2?O>V}jG|qlkaJ`=H%^c$Vn+V z5!xheZXRI@3)~b*JbwKsy#n+905sy9b7*i86M^qh;qM*A7=(cvR5B|_ zbHTZ61cD3$C-th5wMDKu3$)cWjT5K^{+)2+545wfNzMp@53vR#in&`^==!@}J4WA4 z)ZEX8Kt;34^dJ2*{{Y(js=oEJZd^9%U=o=i=ZYS>Y-3L#-$O0LicU zePGC*-RpO~IMb~y?GEi%_8a7n4`b|t4je!y{{WCAQKz$s_ARaM;xD5t1=gmOtiom! z>Bsl3o2|Zv^zep8K#ppg(iXpDY#pS8q^TH#&m_@z`mc*|u}et^Q9Q&Ff8LWm82BX> zY19r(nUwJr~{C`S>vb;hzfdG?$OnwLV%?W#{r3!T*l#r54 z{$`c7PFBV_sswf5HFC9+3pnop56g)D?#q+oV)4ubxt) zB*8hvV-o@b6c-@|N>w1#h4Ue!xda3p5D&dZs5lq3A`(fEKAHV% zrxu#vMKt+44f2iJ7RT7ot^sr^RRTDzvtn3vpylGOTWJb&YlDEC1cT~M2fZTQH#WA= zoyDc7IOuF{S~mzIV-pRNj0&qagc1pfuMn-BXHwz@RwjP)ZYpDn9mm z2o!#z@^t$bZPUEDe{vF#juOZcN%bXPrA5MB*^f}U+PsmSO?11Pw_e<(J8tS+LX+rI z#LuN|kF^~!+xHqu;Q?_tQjY=$=UH~z()*ugRH?v%w2|f@03>?TYG&c9SDUtf?<;oB z6XwQfxQi79{{U$uLZNQli zO}boY2LN(aASz>zN_8#WIjSdpz)R@_D4@re*B^~rO)KzfQA#vES7t3PZQoY*kO5Ds zbL&CsAxit8Q=K05@T7ULyx;jDC5pra9I=4;c5nk|x+kgw(iU(7g@#%ny{ z;R=0?^yw^E)pi$iYVF>x*sx(rPlg*pTTH@7ACEs;ICS6(Te_yPW2Q6{AULA8PB_?r zNY4|BA5qtJ#kz|n_;z;b8+-;6u>cRvQ|?U%a1aoMEG24Q2$&x~l-4CD70)N7*yoRh zU#Bnp{mYlZ-CITJj=8b9v~uxixKU>1+l8gJ!8rv96F-%DC$8)m({)RGrnjk!q=Kag zB}(@9uNmBz;wulkrdCAP$h7Qo#^>(n8b6;w#hWCbe33 z27JTqCWG$T{m8CAxQ5=kLQtk&T8c~w%tx`}k4~WD$NvB{!!N}7m4$!uA1a8_Ql&73 z>O{1#Hl$1`!aaQkIjLk7-K{3!p9O>@3}a}HC%s{DaFa~nCZA-7h*l8QrR$Blvmv)# z^5?eWf%T;nUb2O}`chKg-(Q4<0I&3e8UD20t&5jeDoO+<0*cjug!)j#F{pB2!gp%__AkG=h<7kn|PCE6c*RJm!Pq_*-D zfS|jQ*4SFd2m}rX=4fk{oh!Cs^^x{;m5@&bSy7Y0>??XZOfXleTq#AnXi}5L^>Bp# zIiRnvmQb)%R<-%uJj#z@@}fxNxN< zUw1vdyZYv%t(;mLafPpGJ9n9xQ9sa9+E(IKCIo;vfGDFB)rU!1MUP5#ZWfkW;tlIp0T z=Z{J`msEDim@x;p%A)L^N|HoEdr^sA)%iA!na{5j#NSqD+U}Vql6Z^(lN@_g?u3Q00l?qMWhsjhWJhdo@tg(iYNU; zIpT_Kuz?uGW_{4ss7er2VSj}+beL8+_U}fthy?S&_XdN$+$04=a!9M9yn?7C$P+#3 zfU=bofJlzrg*oDYAkPpzDTI?T_l;Y09yHKvOHn`x8SHCLwxT6OjlPtIr*xGNe=2ok zozh374@w3cZAjBHtj-DaqMJkrR!=h+G{Z&}gU_yJh*HQhM38-GZHDc!K`>9fIHZlc zckVc!YHcJH60DKOsHdCCV`$HMmR}-JlO_Of7DH&Nl{@*Gp@(m~0qN6x7j0)VGr9|SBx^u+m zCp48WaKVW0MuDSCaCt&K=hm89=0TB>kELsJ5^#OyDJ5Y*q>vAKCHaL&Opm>|{KaPZ z1zg9!G?gd-1n1VUDigFG)M`{z%`HhpfRaWHBX;2=h&kpdE~|H`6ojb%05Ra;O%k1; z8sO88Kp+D=dQj>oFkp$nrykk~3MLiErkp@Z2*C%i_p4H_fv`lRxP`qOMxIrIZyV zB$NJ}nxb_Et8b}k*A~bx+q%JpEq3K;Ngap>zkg~CoA_ffRmnEBUeJv zdt#6`6NeCRm1n=LDJPCpx(;qGHjcQL1nfZPxu=q_lffg1pL!d5QzL*o#cVnN-b|!K zk9yM9)-L`@vS(<#j9$Sxo{FY0!=MpE+ipJ2{Rz~9`uswF0!O6aYBGtjtI>v_V6vM zc2}+KzS57jGQFj{f}%TaN{Ub7DVx142dVlTa!a}19X?aQZBZ>8jrfT-i zD#hoSP(zARA3}kRq>gdx-mlu_tG60!Ok0#Gz=DN^WB5SM{CZ%1K9yM1?cKQP@T+FG z+^X2T!X^sLNdW#(YHhwtv{6}d`Kz=CS#hg{0ktiJ!soagA45_Ww^qrz+J)?KGr11P zKqJ%p8ZlNjTD_WGLKU-WTngM}JAgZq54_LHpzqmJHvqkQ8gL;XI;P5!q~ZdHp^=e5 z)pCcab4x8&BC8Zn?T6Pjg zwsTia6H$CFX$&~A8+QOi=4fYj zDj{GHB>Iu*PCS&95CBq#siBv(Ne9A7jCK)2RJSrHuxDLNDL@iQ#0*c)g}74H1xUf* z6W`@VJf>6STEOgN#%ePD`%kq>x84oM`4V?W(yMmf*h`)P-f*B12~nJg?@)TCqi<@h zi&ya#jfwLp{{Xcr<5SeHNJWz=VH|EzmHhF6K%+{wX4?D%bo~ ziOhEbn7Y*Nuhgenow=7>!Uv!Nf_N!T8+&GaXA~iBsUA`F<^?Iw;1w4y#3XHX^)}0G zA!{CWiBi@H=1u_l{RJAs%ljkGZVXu3q?N1~5)a)Tq{je!=~kKx-UEocyA0c-%=4*E zR(aT;N%ieOxv4K+ckDW4n%uW<%F}Fvu*anH?r2m~eewj9Q=~*{SK6;syaJkey*Z*G zpqNOWByd&z>cOJ){*P?SSDiw#TT1q^ZiV2V^Z+um^2zlTJE8RWx3f#P2gX`cgwES` zCz)4u4{CDV%`n`h=0fi7l6*E?S^x;Zkjh6B{-aLm@GZA=g6@9JYRXdL6K80I?-nUY zO58Dm>V^Oq8wb{SsOyK9a_Y5>LBw0Iz)?er1o&u(C8Qqz0QVon3{e+1T1JdZ`lD*S zyKE=JvSg`gZD)ewhcl9Yr9s@O%~eMGQYZHX3@Cwyort9{gJ>u zhU4i;V6uE&*4_=PO{GdiZ7T>1g+$&l$z<^2rQFm^~Sq(B_(o_a(os~ zm)aEIQoexqu32ZRBdI!t%bQjd#*d~m;n~}a6kDQF+Cd-8r7222_2>;hMn;3tcK-lu zTXyg2p%(i_0uhBd1JEW1)K;BC+Z*{a;KroRU-8RTPPcs5PTRXEZL3FaRUpTRwY?|* z0I_6#HA&DEoj<2F-mq|~E@~h7dws*qmWP!fACQlwW~-<_)D3k{Py(sd^}8!OG=HU} zw{kxXp(F9AuZVi0Ei{ckrmWlnHtmO;`~U;;C+k`;Lu?cfjJXu1@_c{rSGCKPIbox; zwS+dibzE)jD|KN10Qr@rJX`6uTS`gsF4m^Oi1b=FKQMLa7$Z1MZl2}`d%n4e_ACRUttsrWb1S%UYx;#_IGsnG1 zwYkSb45UWkBw|GhqC7}wE983$uxWa7K>Jxtr7|FPKJ{(UBOXOvx`ypkr$8i*duE}u z<_eU%jErEO2g~=~iq)MrZ~dLAOJL?tf816&d@E$O!XObRZ-3wPp|rRy@aSX>?gEsR z5PA0gwIteQNo=JhV}Z%~P&R0JN(Dhm&H(M6{{X)AGpE@Fz8!HxZzQX@nf{e;mq1ub zG~&}pI{;gRAKcOz(w5`l#}x?&@bveoJKaT#WJ5+^z(3qjBUN?eB?(M#leiAlT+vT} ztwQxSp$KU1SP(Iftvb@)4TY!~f#r_h^G{i5LGXv}9?l9=KEAao)q`M_IAI`pOjT8K zMy%DEC6vCiwn4|G7iV(qo5^~~YHidXyn;anqi${%SN2vCkO7I0&Zaa=HqxM6%2W5f zXeEeJX2csrqg!8kf=!{0uLjM{xw{%>K#i;c}`eeyuy~9u5BmI3HztiV}nSHW&V+} zmkCI;Szi|v6tftFk|aejt6i4KN|qE={KUtxrQ3A|kEkQW)hzzb`6QR^M%~`(Z|(Qh?7qV|bg{pKub#6>;c`SXOHVf_~t^bu$?C5XaH?g6%ueTBCYmU>a}bw z#cfhV4@osm`#P-@mLF}#oB~vpAf$u!sWRf$me@g5Oc+18tob>8o1w&L>e@b_(pd3> zf>-$a(%;m}($?}-CVd4|^xL)Dv$1MOI2`^$rENmqJCb*9$E?ka;iV<$*(Z3r| zt%S)W=h%K#c3bLt&%;Uv5tf3*vxXr*B)DGDV#k>B2~nr}`e#=%l^9-PrOsLv#~ zS|nXqyGScuo0dB+sxyR{EFu~=S(S$%3DNW`({7py=$-ZvJi#&WC0vSaxSy! z#H6igWgtYs?2zqt-zPlk4Hset)*v+UnD|fiM8z z0gtVBo~5^KES=GmeGKtji(a=LGleD&={~2l*OqylGNor)IPM1~*P8H^2>_Bxxtw;Y z`k*L*0Fn6Bm#IPVRN7%XNCb}6U4)g5gqY@W--`9RcW_M{oWG3g*O!mlxWd$(k0}2D z7$+Nq;DPluwOl6U-N-&V&ebH5wNO1hJN^}~8T;o~a`Q+-?QHOh1c>+i>DJsSK-lvx zsYEib4G>a&O?>53bbhvSom%K3+UoB04m85uXL`IeZvIAq%BfF0<^HObI3czeN?H^C zoDZxKOvQ8u1)XS^%SAxr71`WBzB))l@RM{cFmWdOG_I94Q$+9DXXFz zLW&#MfK`K>f`1w%!lY)#yHCgWAYZrpBfn_2bHs0Nh@`0DM2-)=C8xIPh`n1VYjYw3 z_*OCj_ouGWA=^fqLe!N336bRjV2U1D)NF!oZPyJ0wbHIIV19H+)yh;8;5TyPZn-IS z)W{=<2RZpkq-r}YtwpDVK}(=GB4BnuDE_qNqMm2LMZ!t(StTYinGk=yFIQ|W_KVjg zwiMXhcI89}fIVvdr08Al<%rXf<6kDp0JtuaSa8n;X@IZHADF4$nYTwwbPl<$-E33u zIxV&&$dLf1e;S3mxC}am^!aHcX;_@E82a~$we_#rnCLw%q$O!?_$vLu0Etoae6uy9 z6K!p0n)RGc<4d~!T%2=R)NUVjt3(tw%V`^0K!o^*lO4>*G-bozX1ilf4oBIV2~Y?5 z&Qxde`cUYz*>d)`bdlg$$x65mK5~SQ!hV$fo(tB`H#}QadF2k9s|(RtQtcm=iHIA7^TdRd}mlZBo3TiIF%nQ?0M; z&pB?K2LMt+##3C}jxJ3kdd)m$pqmCIi*?(rN**XsghBI(uEX#vRBNI7wSVSUF>S9T zC7X3B09eRT`c5mIS=yy6{k$;1bpY*KqX7DkU++@7LhiN8HJug0y-wX;D1JWbhkyG= zy>$IAF4wVsrw(lgsj1wPk8U&@TjL#{2#Nnur~swD4YS4_|%4_dt>7z zTQ)S?U^YLQQ8GyU>I(Bv=fH1kYRWpN zaaRhLTpIuw96{|K)H;yr_bE$|AQE$$F4oz-sb6HukH~lalo7us%1}bbjN+TIZ95yc zNqzN`5T$+59-w};7NU=|s3l4bvI*cxjQ+H%o0lHAOJ04Yd)2o9pWY}nJcQdiTsunD zwqkwai+0dbd#=VGxmqnxh^SNU60bHy0U4r2z*2&evJ&tL7{=}h{AQzX0##~~pdb|} z4|*QP@e5?3P*lQ!SE)*|+u!x3##ZQZW&1-~5b{=52||(qKkDFql`@B+L8*0jv`EB{pj7>KnWwCU*DxMamksc*)?vjC``zyT|HnrlaM(v-k|Lr-L**( z>OrY{VhIWuk4)7xpF-mHvhif4hLWiRyqb_|R3>6LsEsP&E?cE!f=H9x)UrypjFa-E zN`rjzEUhv3NF&~(@3#<1oPsEo<;fdhj%X{jz%Y>s$Fisw9EPgFAQXbE$n>U?q7(v5 zn9Vw~xIs|^-kD%*Q7R-3)82{N9T4OIgziwu;}x)XuXLF2S^oe~WRpC5R??EA0FE>7 zLgQ}Pap+rTAzk7JwGx#DN!vb@%2KV#T#yLhihK}61BCI46p&VoNgiTE#(2#wB!C1& z^GyN?AgD(omWaovGHT-<0$~Y*`N6U{s0fQSI%nRz8}MhELukV~G%oGD4p4`{9K z0|9f`(!zGcq{pWeGmB7Hl!MO(jWRA>3t5Fo%8ogvlnZdb{z_4AkXytl0Dwq6rl9ZHUfe1VG3!=0hTtRv zBLvh{@G-!cFnuYJXbVW8Yo}vG%)uFnk74t!Th(vsT5ZZTYkd@6S>GL(z5^a5#ko!c zh)jU8MkDK8Yp89WSW+8GNK#K2BB+*Hs%@^HwYs(hA#kn6VnKrdO%2vWg4=lA9A{qB zU)2NmI$MfyfRR6Gd881jl3}wT`yA9yL-p3F(LGIRdv&8U>nKodZ>*5#g0eS39F7Q) z+Nb_3XgwnB(r#?7EwuMvE0$YJpgQ0(pb&)a3XI7S-YcAG;gYmmv!`4zjoW_Js5qj9 z5CnvRCmH9ktrLdfD6zQXM)i30+664K^USG`p~h1BL^L;g!~`)ho;!A z9VK_ID)Ykw!*y_-rz30ufs7L#(_O*0lKD!B1t*V}zt*l%aC<<-nlcMQ;UT~`2<_UA zVPVAW2i`{&7?4>+p+jdW{AuQ0+N7IhV88qvLOftME%&D z{VGYsPT-&+UrHUol@uWGeFi3^6>tkcR*7XxarL1|`$`LG#uAY}U(SAJqAm3FTIt9O zK>q;C1za-&I|trR&`nJ-X>n%KLK^`kYCah}l6gt(@})Y>uw@&9!)Wrt#2-RG16LN> zYNu_RA@#nG1;WbImNzM~XKIN4WD--y ztXE;VvQxTi_NrJz&7iP13Elw(823&pj_tY-Rir+ot?&A6yj2QPc}93UOpn5eG`J}; zi>Chom!;&}T)rD|$>CCkMsQ4i2O_N+E~Leip__#=;!to3%95Vk0~3z*52@&kjkii# zh$JQCtZj!UX~$&)IpDykn=L`@FH`U?lGeqYfont*n2=9mAkaxS$jHjqCvDPh()!X= zvUfynPUG?efIaHg!jiC=NSW@e{{Xdf4F^&eDQwDsQT{`<9_C5NtKO4#$q7PK0NOwS zBop!+#T0l{1eZ384&A_&1DGiSl!SmXM1ek((`p2L(t*#t5YS4!50(;mQ~agOa6GlUKG{ZYDVR9l$@CPRL$0*X>5{&xRjJg z4W}g|(gi_Xbt`VTwwP^5N@IJA01uF=xa0gmTYUvPr!>oHK`{x4ov9v`8+5yS6cj9^ z9zsy0N%S1hdXA&0YK$qHM$2KMA;NZ~iQtpT`BW=bT54Zi)yj?2jSa_gJfU27=k%=k zH45n>)LQ&Ro!h#rEfy>-TWQw}t!31bk?2)8s{6W2s%)E=7`t?tBsc>=&#xwj)Gn8D z>HAutDlyi*-QUqhJC{YFI*BPBOFoC=zpa%InDc8dL9~D|PfEZNAn+U)FV%@kiYV_L!hi9#>#v}{{W@1GqiVC zK9ym%>7?7CH(f_^+7R9n(gxH89$`F)$Eh@L>HLMcR}74}V(VB_HtgD~>|g@tk0WwO zm>^_MDE|PmSm}*!x^Z3`Y1)=jke%i}?mK%Y>rUM2_N*wgr63)^q!_|+_p{H|p>D6O zbhZ7h%Y%EUtp{OJj3Fm6JofA;)xHo4>VmZ`Qsq|Q->H^uWE3(H1qqH4j_2p!6&j6G zQM*#uvA6pTn$k8pkUrAkIFD2&J9nU%dsNj~MZ(zHULVACCxnxLl4cTOWOGK`iwg#b zRo453?g(v@0YW>I+ut8Lrm9vNk30;p+AR{cEt*@!N>N_p6FDkUkJ5%O-D)?ewzj$p zxpqnmXaOvatNt_IYpO)vs}=_Eiv^-4g`sWV&D%I;bgjfFPK6}j&`?Gxoe zKBv7g)l9K#py_&T<;b+ObHvnj8=1weqHv*}R!ATZaoV)T8>L7I$w7Tb5%`&|wbA`C zb=21+Dg8oi>>6AW;RnvM{{Ud_eTeRBgkCdw)BQNM?$lS*omjBDvVZC`YEI;`eG1Ge zessyZt8|qqM1-U( zFjS~YesMEh>=^yHQ#pAj?#}-JVq>6mue4lg`W3Tei?_q>ueo892jmLjBi4QEsC--2 zT+^*}23tGa({0<*ZP1l{`|E+{Ab;ADN6=Qj4d^y@M*je<^(299Zr|Chu>-?v%(6W} zRDKoDI@hQjy48cLSW@)O_s;-ixv0O!JCrAA`GM<5m;LO?ctcb6u{Qf;K9OXLT_aR% zjqPc7mQ5jP7%m2G{6jt9DPM@LX|G?aS=8UVQ*d5XCon+Z56o9e)m&bK@Y_|eNh)<$ zR;YPNRFy43(z7Gbs3v`@ndmm7P}lU!7dI&n-87X(AtgdUSt0UEMAC2fEf&N6R$U zuKB#wUw6y~@&uj)z@{$6wEGxrtdi=9-M6vtRvkeKa_P+}B1;T5(lgv77z6838g00^ z)Gcks;*~s@PpAe@})^=00;D@-epN_$r7*S{r>Y=Yi$Zioyz%f`^`xFi)59ftMI{6NSMeU zze+7GsV$J9=RJpNMVcIOA`^s&$%*2n?JQK|fT;;T_L0Z?_WUT-F2O)eTDx-NT!2lDOii^~>qW=K!?bif(AQ};0XuDc`XaMFCd(`EYl+z7`IFuFr>G!3{-_nOd zHUnvIaNRM-N>Z}`dwPETD0D0^!ncjbY%9DU+*8D|jq7kEEaVC1KS5FHxh}X{ zQSTGpDAL=&4ptJ~@}4Crk>&E^zwA_wl7)E4bqZ2;dB_+uh|kU|R#t!sCN}Xrk=rz_ z!p%h~JGXLAZ1$m_3y@FLE=EgVu>0I6PX=QW4h+w~n7T@&wV+KLpE*{z%8qyklL=u~>+ z1XZu4ntAsS(~PMq-4H|(>&rOG}Dp-a7w*_=k>2fYxc|Vmmow)&2aCCmh58(;&+`&Qj0Z?a3H z%RNlkMeC*ql|%_R=9%f0Z@B&sn1cZG-lck${?6X<)i`$e*UAW zF5>0`k`v@WNRHSXSFXnD7e-0Z+WKInHjNFcAg6(WR*P}8m6HM|)=$!_dIX}?vJ)~h zj^8S>Tja{7QZXa-t`@!ATOWZC9Z-~p%48Bj!LC8A1*zu}Pli-VVrLUwd#aYN2|`jP zcTalc8s_*>3EDUZ(0a{ynMZDQXKEI)baK9>+`ENtDgdbDkDe;D6heSl0FffB-AYMy zO286u2NhZg{ve+*oXvYpFaFwS;^q8yb`fp4)jMl&<7(O*Z7MJnkRd)caOV zCBUGU3b@)}$^2eN{rrjuKa$HbYzz6h@YD=j)KU2}P7O$LY zbo;cVG^DI6%g-YMp^$EFZI9X>N}BM~p<+Y!wQL(02dy4IwnB$R{4IsB>HOV=%$P0~ndvgrV)kdYETR5s(m zzG-bzLv5|sG=<*>N?bzD!`K1G(n+nh7k6-zcKBZQijq_yB#-4Dl>OD^z2%L|PB6Q0 zui^!?2E!y1DgMTpcH2-6HoFfBV#evG>3u0CH#7 zikJAacE9{Vaiuu<@Ed8iiaW;Hl74D6qZ;Vq`lMcL@PvC-{{W%QwY@^yQfZwFO+t}p zU`b#sLft>iwf_Lze<1>^wyq(UQu0=z;r{?H%ZerRIK|I|kW!%|apRg>ELzK7D}w^xrd ziWok~7z5U-w;e$3$_0(06>i4gDpGvge?gwYgwuo9dV&a3sI|58xTOC8>K{S#_4oFs zX>zz^;LonZESUFCh_09S{Le*vHPr4jeMqHkc#PkH;Wm+kkurZCK2>?uZ>%(1V{o*f zrEBv?Ge2~FYPZrlO~!-MR{Hj~fW_U?0ZU>6nrjdM>`p1?-W_41sk<9N;o7B6Z&ChX z*mpj)+;Vxoh@UF;2uD$=l<|HQ!)u}u%~4u~>h4syN|JV?&`m$RE{_u`oB{R6=T-X) zzvHc?l7*o#s2oA``c&HwHjn{apIH0_b7uyHs;&jh%V}2J$&7*SYae|Q~elIVZ{pQST+^L{Xe5{7_4fiWDPsih@EyFz!T#40OEfxDWF)e2G)mfZ3r zl}YY>Xt3%E%7zF}&VbaVXs@=kZl0iIA6qlS=*;HXW+#5mkPkLlqU7!?_=)GxI1l}dLho8j& zh``K6SeSdx6qVyKKQd~A(>jmZ3oCJ7#c9ML$v=2Vg?~ftD&uJWat;hCdl@v!S7eg3 zT9{aADJ4Z=LO~w2A{Ld96kx;=^QaqJlDl#ev*j7>Qg+KO6s#&X^F6(2d3_n0qJ4z; za1sW8mMT@F1PRP=Fa<)}x#Ft=U}q6hjJO8e%+nuSiA$2-qP$BZcLRv2#@rxzWY4b^ zR?^)d2>xN(rqOjMg19k(G^gJIu0s^RCg84c3{cLlK^s8r%_#~cJA#CK=w@ui^w^@C;Mq~aMhALU@{l&1`qL|b zXC{3s3W-btGe!CkdwC#|NcW{FB}$SY`?%)IUib9lBM1o_|m#`#)R6vQx ztys3cM5yggm}V!MWqU}>X)JuvI;T zb4-+^%18uz4k(q35}2MbOt@h@14Nsk*W@td?vNxNdr()cf}8*ZM(o?e^BFbBx`NPHYsbZCxYfvokfMMOB4a;V?pI6&5(;pv zMRR_=(a~{grHzfcgSiDt0F@3g%~nOVS`PzTs$Dal^$$zkAY5x2oy&!f2`SrRpdASV z{{T@(%mhh0aC?g5{Y9t^dF!1sq^<7Vv9L>N)Lc!^kGLSnQ7YQn6UPQrPtv;Xv#z0;SwORo9Qrq- zCyyzb^k+x)=9HVX^wrDh7kdkhg6{LMK$Sr9srO3OFBYqt(gaLMZA?X zh#5gb)tOzPolblX~ER}VZK3RTk9QdH#OY9p{S6{{qa;JD=HO}tZY!S43l zPQakO$sl=W^rzB8g(__FIrpw3@YmxepQ6Tr*POlDATlsVWI{2rQFrU2TgS7Yt( zN=SSxIFLe-fPY`51n~SCBReFPB0xdhas=~3y4gvEBq=8mJQ~{C~$sW zl-0wFK`@QNIhi${QEQQkUI#XXf`XDJB7a&eF;!j5UI}KbNl(#YpN`pxw1Ckp$bchTO=wJ42gr@K{)k_vEMwb zcu-94`F=G6d_%}mUSunGB#8j_2 zUpV#3Tzb(iIF;=20gnihAZ|JD2b>A12IXifv_E9UhMMXT_JNYQ>O~SmTT*uxtdtwKWOyH0^Rewc&qL8J8Aw>5+V~m6T)nT?< zs|l0=$8H50X^WMFvZWbPyvNGpr!5+1Cj(yC?f?>ahc+VOP&J0M0jmEmn8cWG}&nb$+V{v9+dLDgaCyUlgOqP zt91gB>D@R<1b@v9cwn_`jlEJ3vng2yN0k1x2Y0C1ugcxyi7Gitfkp>%dsUY3rAUpU zPGk<^s``^pIu_e@%`H>qBxK_~sToq+!Et^V+uFlzqy%61yaT;tC+Itxhq%_CxGHcr zC`!KZQAj`ih^;+h-J-y`ahA7{C<-}OX~9wYR$RTMFxpb>tBzd+leH;68a+2~K8CP4 z&*VNYjzL~RpKVS&!Am{2ETj^9$A7Ix-Ci+npqp?USb&&F*gIfyM|n7WDlXJ^m{1_`^q~d2 z*9nv$DJn8nwN5`AoK)*tMUyWb?X=~=jl0hSHWof%W=i)XRMy9}V5=ua@o8l(NYLtJIaN^D$EP zmThUMRqDTrX|Q2Gvk+Z1j~@WcyirZCQ{r0D5* zX?EKSNS7D4aVg*p$2sf;RW;6~xhpsK3^-2Rjkd~4-BCPk6OVNqd(^(C{!eXgl&^Q39`m64(%bN>EEiHim0P%-h-cbiU`3KOb)$FzPk#ov3OVav} zRcSZ+n=NS?YCOTUzx+#uMgrY9A0z|wq@5p5al+5olX$~mzjn8-Lkyq&z^JWaU z;-U4vvlkZ(STlEVcC`7+ib#-1|JKx#cf9zUQ0>2P2L#2Z*N?uRCMP_tTS{!0`-w`wh5#I&Km|E)h7iZvCL3xJHv#u@NhTvd=@};+ z3OPU61eCd#Vl4Fai+1lW5SQ(mP%J=_;h0DxnUbiU#%fbf54`)O7BJqNoQO+v$ua!L(|{_$ZT3W2wBuIr)8}$`%mR0J&k>l4Kftc_bBGwB#9;PcLv@l5sXK2Dd)aeJ3PZP-` z(WyYZX;)3&w|C>p(xoW2pnzbhK){0l2q(*d7@_R6<+}Rru28nwT8K#?1uiq?>?C*o zU!6YJQ54%xlX^Cg_Nj0!m>X@-qL*Az!2>GKB*gy!z^*<1C;G8^&a65QMZFf*t3n*q z?ov{u+uAo9QzMrYbKXo20j}}X8oNCy)7MvsP)*V;gKS&0lO7|d_-%r7J7sDoFg+{C z`lZ0Wy}#7oUyj2rZ*kHfT-^TvDo6dP>@(c=t=dR^PE}@2ME5FX6JL4OZBeFJ+8bw| zZ4ENk(xYc&pSXg5)~PE|DmI|c^Ap?AUe)Kut{iQrFRm0`QdVC{=fr(R0r!I>dj!{6_`j>ab!Ba0 z%ZKe+{lczXZ87{b2}%==!2l2Vu3LEByBAH_Hg_{%lA=;Dr$7Li^aGEOpwlO}bMiA# z?WOQWYL`yjX&UyJrFe$U%H7i)-1}o8S^og_gj7F5St+aAomKSD`F)$u0%OYN-KhTn z?Ub6A(Jf8ldX7@Hm$dM?)0&gX8$Q;s8TZ*J} z+WM@Ews5ego@|iY4X6wrNQ#C+9CiXoo^m_X(6m_6obsK|xF^kmk>B;Cw{Fe220>AX z$8TCS$S&A_OX-Pd-s*1ozQbnYsaKkgMyE z&>f3yiC6ysY(-X-l{qSLK&X!9D5KLOd{Ax8!tZUviC$YfN=Ky?WoERp9B}416yBQ3 zTMJ5xV5DQY`PAAz&c(l&fTO@Ut8bfRyCRSrUD}XSC>&$zKN^#_(b5W5pi#iiNN(DwD1rzqg zGTAC_R6&&!Bo4>YqLS*GZ~&kY+z1q#+tnxrk-L$&M?Rz4n7HK?Cumj8$BwfD5uLF` z>AHGD!C!L_M9_@ct?NZPkV=#7gX{gOLekFU4-l0=Aej7)epO4Jz~r7xypdS>BGhQbM%7$?6UkNZ~Ic}XAul6VLGy=ilohBq91lbU)1 z_ab9pkbT8XtE74aDdW285MBk}&2 zuKTW}DYYvn(zs8=roV*epClNA#ca{9trYl2lDfhxXQ-%b9 zME>{`JT;zbA^2-1>rj zJJ((701Imkwjgsekz9jb^QA~iwWWkCj$}_N)U0ZtuyjW2d!^BylF1nb)m%vQUfbhTUwS1n)AY z*NFGbaA=D#FW*h7SxOR>rArNn-u z27Iz-v7T#-CFYvbn=dpLmfR_jCvM>xk9zDs8|*bg;JVaoE}F4=O|#D^DGQKr0&ov_ z70C4DMws=9Aa6N>NcBwn)~$5!CfO&)`DYW+;NG3hRY|wUHGVsP#9oYvx_60k#3U{PMmU)HRjWvN zUfKjLsY|V?nMsHW;Qs*jDy;C5pB6h-o;I@lc2l(mn!40(EP|nFg%$2Rp5IDkyHKlS z>8LCFLR{M*Nl+?)aZ{ICYqoBw3$jX|Z0vPPS&*Ui6YEiHxw;R3fN zd8@Xa8lg2fsdhLSc}aaY0SqN1B&(97%oDU3A6Tt1a>G}Rw`#yz?a-A67IzXS{UaWj zGwnmA#;xwxZpwDAB8XC7NKUop}bC`qoKvAExEZj4mRM%Q^`-_DUN}A?LA3t zc&7kU3sbNTAt_M?lkpvZHFT*v)-GB>&ok}q{i{qQ)J`UY zwGC+ciXBkel`i$ANGJaQ(1ZEbHK}x6t)JO$I)44vq$s?-T_GwQ#>6rV{M0CW_1jsg z$DCnHk+rSIxa69GxVT!{D{kEYr`mD->!TO+@NqNXTbky-R&-9D>YWE`qfgMw45ROV z7=@@G<&=Rr%}5XO%lmf%wB^hbmlZ!L^{#Dgdv$1*7)sWVp}oLNbHJ;Xn{RdyE?m75 z>R~A-KQlFxJX&^jeJw#Rr@z&b`fi%bm_d29pXO7cWjM@$BzjkC_(SmG#`$gP`pW=V z*pC(!Ftvk(=bm%(u5<9$q_qyE*ICmUPw@ApcMhmwDgMH{7g}|drInZd7wM-`)t!rE zIO=c|VhYLho;~RBsAFF2{{Vr@_03j%lKYOC{ZE$u7yS4mI``r=zOk>W(@%6J($J-# zQgF3N1P@;HcS-dR`4!?EPUTs>8`F{!c%O1>&pMW<<+bZR5|y=TQj$+90Km`46J0B$ zwNZU)8F^rUIV0Qm_55qMq0I@#*KDjFlZzIW>6$IwZp%7$z5Z)pNVdI#n1SXwKcS>q zUZjAsqEY~e9GYWL->B%W>2Ct3Ez(zRNcP7cKgz0lMz*4S7fA)OWDjGL`}L*j@u<$% zlf`Ou($VF3r{nq^8H==~3VB2l+HwB?xZqHaJOT+a6hV()_Nc+AC05NPK3@~~hB2N6 zY+Z%GDh6^vKcrVTt_Jg*t$u z;&XsvskF;TlG8Oc={K$3pamr)l_g4mr22kep{|tMz6J8aR2I=%i)!^xr_Os<7;fz) z1qv{*I3|8`T^mT%lI836w*a?5NWTshCvr~gl7&Ds{r;Ymck%-GXF;%eKg}?4kLmbS z#h6QIP)dvw_|;0?)uCZ4jQ!F$SIGTp#izXq%2JSUNt!83ix)U~ELN-90?9Cc6ZzDg zs(@JvI5`nfR_-Cd77j7mDC>7NxD;`>8LLfI8N8OAG0-+3L~-(|bu1*pd!Jga>CTX( zfdG?}im@^h^ImM{fMC!o4n5eUIgpu*WYH!RkWX$;r3{v(P=Ej&flsCgP>BN^)cnRY z*p+{*a5?%4P+>%pqbVG~;{BWYgOKJ9l8t4J}(3xZu-hAO(qlU`VG_r3EH? zoW*Oz3LZ~*`PQ5%DcYrT+tcu(QKIZwY@h|?PpRUT0#afDGcss!;*fnwkF-|#x3WP|X zQ$rG|CR6A=>D47z{U?fTVJjp6ar@?vq*YVkP;dmLC>)WV=AABiB)jUXQhA2=qx{0;p_Yg+grU94RPG>dR&xNPV!aWl=w|RKOH!96 zR1|gr?j!owIO_hBv9+{ucFjK3v+GE>Q4)s$JGO)T>LntoJF)&AhB-$fsdjLS?v!oj zlhM}N)xMrJXMWLfaHND4B`FJ2#zEN1M1-V~l?53B1skopZtJF0n_V!4>8rQ zmt8`R)I3Ka1zWZQ7yy{SuD7c6{;0;kuJtcgX@$E}r=j%Q2FmyO)LCq#@I0sm10+b0 z3W1|IDT4(u*l!M7L5CJF9{{RO*EV%ZUZpO6x&MMihuiYuV zt)%XRpsV?nwh52wE0EvV-F1T0jWwp3L#!_4x7@b!23tZx-ceTH5CtjF*a!&(0t_V~ zCV%2fT{Y_+15eYgF6}hSQ1Vr?Y1CW+MFA*C@}fz3YbI0>6l5q(Zc<;ED-@yU={=QU zsM_ABxWc?dE_SMT_Nkj@khYYy5h4ydSCl$mtu;=S7N}OBw_0-N>~FgBrA?cXR<(o! zfRu!kh{qna(RAPO0jJzwx1)NvS9_=^D&++|_RkYbz0*?LfoIBn^A(xT-TWr1apFM-QEZt>G7Vt5 z@stk1_4S~Kgp8&Y=@gY*D65QdilvFA%L{O8sV?q8Z27+$7Us+-1b~s{pD>iq^rhQe zZXj;V;Qs(xFeE7>c-s+^pZTd6rNFl)iVsuiX-1!Lw6YTF4~p89Hz&lz9`JAmIr`Oq zO}h%ZeF>z+WqNjA$RJ%l7DbzZm@gQvp z+PQ+L^^AH|cR?Ot%fYhZH#VT@^8|%J6X}tkm04IO+1P24hqvsV+e^qCg#chEe6lDl zS17(y@j%{i608S4F)KlRcs12$ey=rp6atB+7!zdEk zr^|pv3^f~+z;@NANW`F!e*z=(sSw<&a)21b7~+uX6SReGDfCjNfyKUol#vekX8B4& z?b~c1&z%61LEbb=V%E(FU~T^Z)7lD;{mQ9V(v&2Rw4j8Tg9jfoLAbUMg?aXm`$-Z$ zl$?b7Ta+t z{{T{z$v)&y(xWae9k^IvzP~7fCK3SdNCL9TlHj!_M!9IsiuX8KE~-?Jy`w(3&)4#% zt{c3$X(0}l(nOUYd5@?_{=8AI+gzO5EhaOr-C!(PjCRAdXDN1PW^U%EtBma zy2@5f!j#)=GOh{o?kZGHV8nc=V(Vx@*6-Ef+ptNsW_-`Of0@M}F$2(T7!plV?lq@% zZE3bhxzz4ncXFF)xMiS!okV$msrRE)xrkdW*j}}$**E+ndhPmP{!$(t$Sk7UuVILj*HtZ}uw#;j|9N90T7V zPbB7h4)iP>i7Z2tfm4VeHB<=g@80WmY&`W3Etu&ZoaW2m*XE?L-=w{rE_3beFZwvgB$Dd!V8 zl13!_qZDsYrsr6_I#@mxy}Lv0E}{SlfJ13iazKb4gq#W%+TrbL>hDvsX_iv5t5iC2 zvedy?^iN zDndcq9fAB{C(|e1PSGo2!KmCc(iWfk2y~FkN$g3CjD680p3y?TdUWC9A*4BQ>;Tw6 z+Kl2%W7p?YABjC|u9ni(aP`yg>NhGZ*`Y^q1oEit2WYJr;cYJ>oVaX;_^;JY^&Yr7 zSEnwWdK_4}eNcj>r~=}Z1i%Bd;V?VbEY}{hONi5KQ@YmF6tJU$wSO|tx&0{DFICg* zEf;PnT8-f*ryQQom2P>e=gg0`_FC1bo#)k9S^2n)Pp)&5f>Nh{vv?;jQf%5 zCam3As42D_(=_)>b{5Qil?ns?ngE5L{xB!hn$MDMa@vODih{S<$9j)ee)Cel>PH(Y zcI97Y)0>G2Xc#L4+>h3%_d0X_0^9*maU^7(RBHKp^^M3?;B11Q;oBe&P_o1}yG3`rJw_|B2Tc9lZ0A%CjDa)(!1nG1;t~l!r*y%|!Evx+`(rNpNgNWdBPkHUkwa@yTM6c1tR>sJ=2 z2_Tz$RVCR!a3n+wMQ-WDq^K2`OnFBWO^sD$l`_@}kr5e+8sMxUZY|x#J*FyOG!>+% zW$UfO$UgRY{{VVnu<=@6B#@FmU)qN^2`ec80r=<3>+(kC}B(KH0 z>1O0M2WJQykIpCcpc=Ds@yslw0l@rfM&8=s*{PJL?~&dKrQ29uFzF$(kTJ$TAKx`g zj>~DjM(o^n(MfG7Bopg7?^CL?u+>=%EBI~^k_1rBAX~DKv?VJe+H>hNn+toZrS4>r z0t9jRRX1wGgRaRNPKyB`r8|(LJAj-DG3^b@OK<~9^7oHgvh?a4v$0cmg-(N#J-r1H zsNXV{<)RnNiRapvy4^92Ca9pdY~frh%ZwiLNnMFrHBVohksC>zgFoBrOUnMA%+UqCS=;^ubm|ESee2VD zb(l(9gz?Ast~KyZ$TMWzA_-T^72Yk`PllhARtZU_W6Su5U?s3hqaKwPc=pjg^(>~& z$ccf#sH@h=834s=6%(}AJBxH(*ZKZDs+DV=&V%fF$LQD~i zp454wycHD4y=VopV+J#d;vW)`Exo`7eqW#Oitc*5f5K6TG4ie_@ev9rZ~#v+*i9Cb ziJ+fco<{4IO0S-R0zjBJ!9Jf#6QV4_u9T&McE}){MAY|KTPN+Vl#mEfF*Es4y$*ow z@mUH0EDWUaKfG76-ul=0KQpqF`VmSo%6~=YuRtXAL(22x*MR)$RvLy;g z0GKI(>VJQYaW18?2};5U5imP{w-x0lhD$ZS2VWfSCl%^QOR7q{OiylVyoQt=DJdc( zf$9CK!_{DmrIo8_kQReC|l}R4W6*mn$#59 zQik0L^EQ;p5_{x{_L}T2ba%tvs&3m>=vNbMd-j#>oxn*{k%5URm_4h+>GYU>n+@ zbG7z{Oq{B!`ZvNZ zqq);q)X>5kx4dvPfZ^Jb;QOOI5@Zj0^t=v7R+_>xSI7&*DobxpqX3yEmtw1VJaG|xy0ZAhx@ve4E(VF5ZCDG|IX!Ax* z=AP^C;eMaFdFov)`~3mkV#(5O?UFvwfSj$d_f|9M&&sKGiey|grIN7IN>EVui6`rd zv-D?DD&NAfb;)@78Y;?2k`~&hu^sDOS<^|hxmK~IAtKF6l9b_17&4RX`qEo+b}vmS zX)?VwSI2RG?ft)EpwLCKubh28E30iw2*I3y4QZgJ?VGex6g*)rEb^B9CY-&sUA^JC zT9D&PwHY}wO#c2`1OuI@vPTJ3PARI*GZwEC0jL0jC}Xex2N5k}(aEv&hEq4x|#G!NY$_Z*q+T^tf_ zHqKVLM;x}GE7n`m?^JIbO797T+nFJ#z$=gD_U+!Mx+_Q4dV^DD(WaL3&o9V;OsY>O z&<<)3ru1D~sdbiYG}~7$Zr@by19Cv_PipjTlW(K^66uEsEZ%h$(qT&46sC#nC)1zK zuPote)tu?-<)w;P`J3S{^T8*o>y}>)^oF`iOAR}$-nX{p*8c#L+HuDxH9*(z5cR7z zY+NgQzr=aMaH2b7o=@Xd&3jh8)RwEHtxn$EIFvl_-~z4(pw4OSUi}NYN-Wj`hC--SqqM zs9A>_U!AnhK3x5Oy>srOY1=JzF1CV{-6#^wE0MFPn>Wp~&JaQK35uPw*Ni19Sa5?G$%^G<#L~2QUH941lc~%?)dY$o zQo0T+?IA)Hi&oVtd1-GPlA{CbG>vNYE^C)Jt%RhibS$ zSsarC(wf}0`G-BDHFmlRz6Z9fkG^BlO<@xdtoIeBR1EJY>M12ESujWT^wHXHGOdak z+5+*2Tkc5#bk`JWODSQ^l+lw?VRO5?O&Lk1-h@h9I*#d5E5*DVxJ|l;A z!O4l@rPi2F2OY;X8LHd6cG9fbByPggasp!`=4py{h3&oy2B)Tuqq@1?!b^7>mW4zX z+CpJD9pogBbe~gDn!iX{T_buJbvAcRG4|$=sFsjczYI^Z*CP`qc_O-U>{_<9@J8g9 z07=?1L`UgCJ*Kp@vc!;ttSLut-Xnm0s&3>RtI9ZEQgoKLXKQtF{{V+ub-hKV5apn_ zLIS{AklGfx+S&nhN>e+Ck_JJJyXl+mnZMWdcUI=0=8tCE0mqbv)#I=Jve1U)Av-QM zp|OAzq4V$VX{|mL;~*$Gg-FKH`Q04eo3yI_clk)@t^c3H5A}n#2 zX9Gr+d2y(8y}j!QaVFnYzq&(i63Sn301HZjjnCSb>D_7P! za@i?$)hRlbmvMkT)X@b=2ma)3Gw5iOjMIpjVQNXE)rftxtzKRU2ilsud6SnSP60dv zRsNmVp4GH=U%3SjhSHal5$BBkYj;n|$Ww#@Fb5R1QRxj)u$L~`Ss}TeBzGg;kPh{2 zOc9KcK(v=0S@6ImF`g<_rWT#6R01N3PL;CSSHPx_P@gVA9L7x^&i$c8Ng&c%KroOw z2elO2ej1bW$)0O7yS5FlBPwsiNfVCM5vpn5XhP6POc0=Bzo@Grwo3fN27RM6HRYPz zOsjU&h>kH+!AU;{EVDi=zCp-P?Zq8y_NFZYUTf`$Pzh8hg^)mr z?;w9SwOI8%C3lMO3V)82$_V~=^r*`#AjP`W`kY%i1!+tzhdoI2?@Mv#aND(ET2`#t zVYXg*d)o4$;ADV5)O};W6=KqY($Frv7L_QC#t*(?s@9fIvhR+CA@zOOQhxaFxDN5@ zRvjTBDO*anQ!S7@q;5bn$UiT7sHWXAxNCyXZu3POOacK+1A+3RZPH_MPT||qg0y5b zLP9|V;Dp9%MXxa_QIA7f74(5_kozgzvaG;3qJ)DoK9nj}TL7e!jQZ1o$iX2-CnBvr z17CkUZr`1KhUH2mKqnBT8T-)#wDz(ub2p8&+wWf9A1WF^QqqGH z07xCrr>P>W8eNm+E;n%Vn*hYA=>Z3%{&X95B4XV<^sMBd%ByT+21$RGp!Pccjk5|N4T-?e6* zJO!wy#d%I0zF1R^DE|N`9kKq?Nq<)$H08;pH*2S%0{8`~18`+3Dg|FD_wP-8uIBG* z-ep0p@Y4gi^&m;d&*??CVzYd9w8{?B790U(1F>G|h@r0a%ZAb6X(Sgx+N-jSqaD!z z1do5n(>cb!F;3;tw->0Aw*JUdovU?ZA+!&5Vm?WeLxE*(w;m7p!2l&{C@4uE=}LKy zd4cnw-g);M4+nQR&Q$W%3-YE+0gXR1v^B3x2oG{_6yXm+z1H;OrZB2 zlzxAma4xde)-@y4T8Z6o&@E}qEKb65ULgDN@~aobE~Q(#gPN|O?)F$vU8Si$SDue5 zmIv~rOb^PqhO?|+>i0{zzql?sRgy z^De1vDO2Y20pD+_s^bAB-^P3Xl2hg}l`F9M*HQRs)6Y0Q*Qq3cf#SFnKIl)~MEt)q zS~E#SSgdhQMZU;6py-a?Ejx8BOa`qI22(hZ=~S+`yrY-Jw5RfjMo;#8CGgeNa z*AsHP&Y@a%F{h+kS)>dV=P~Of{#C`b&27a$;wYW&5~UfFG`v{$eM_+O%7=rtX}MC! zNNs`qG?^pN4Ej*7Ji=5Eq6%D{pqxf&8i(=h(1f@`h}@ttngygd@(6juDH#$tj2hax zR%36=EIl6GwZBo?>WWG5?43e{zy-yuApjp;$9Mk#VrrJ1JX_j2%j+Ra{unvI9R6Tc zxx{^oN3gJMDlT6>FBcr_K`3uMv*I79s2;f<9>0FgqC(J%mjV<9Ht!^3wA)q7lVl4{ORT`wDd;E5$bESopA8;D1b|{{uJ|ddc1gHy98!=I0L0`>el!Zva;G+AO0nxd@k2MQsSYK@;7t6f z^=gTECE%c`pDYQHOO@Rq+p*^KbuCIzAmU;$Bigs%ILf8OW6d)8UaHDRlYv%7pW(Z!vMK1|0RmDE}XVnoUJs+UVJi)&OQ zj6jfT&copOPdo{vNw#2;ZIVbxQ+}7294xwmB$1=vYBm0598Xn=U!PY)*adi=RT9g zc0E;LNGUvUb6o!bscePFP$QCO9@V2wHl=L09w_ohRCM#MUOuoVaUuZV_oZ}COR1}X zrJ$Xv$9(#GS6kJ(V|BumS)6wq{uK6!(}AUeP(+`~y$|0$ji?W8v>l`A?je+fC}2dA ze+si%*^tmAVET9Z)0P@{hLAQZ%fQ*G}u9RC0vo`&f?0QCj%X4Qwvijup6+B`yp_f!r=dV$?Hq<$at){lE>eA;fU zmK3*3+e*oe#yC;p0nAlr;NL{I(Y-s>7rj|*{gEssTLMa4OK?bWwT@#vaTUXQv#PZX zYhD^tn!C5w_C-odsvAp*6SSVw2E0x>^t=jEjmlp>57IitKeSrcrNNT1UefQ;acJwsD{Y@F0125L#P{{DF<+w3QV-q`uOJEqYVXWfclc$d z^$$?|Hono9b!yG8inlEqTo)GNgeaN*P$E4|aJ!~{#i`xt$x2e&FNRm{M2ve-dX%M| zLr~RjU(D6^b5D=c{ohYbHwky2xpKnTc0rO=FbAavaEHT0whD)tX^bcl{V2u0 z%THWe1?+u5DYT_B2m&NiR+l8*Cfp+Dq0u7C%4ZuGhb+G+r|k^ zz*z_>U*1aMBp%(U;;sH>Jw~1yg|+gD{clSWU$O~PAni(KNDy)FLpqn7eU?y|N>Ds_ z86y$<_o3Byc+I+&g()trdo!3I#7E42buO1$ZOOi5vX@Xdtv`IIdJj(1MRAoFZ zDe?(zKjk`;h+v)0rI3_N`9baVqwHI?drW@D@kJmumz!t#w-2A)=8R#@F*<+Q>>pjQ zxFtnvOr6KRIH7dCA4o;WNY@L6X;?~-(IhJ;^{DMSXvLdRk0O_9ovt5(udZIZcFD9u zX5F;z{u)dqk>A|VJrB@bMb<_3>}`I}Xp$1`+n}YSiR^!v$6;M18gEKBw^G!bao-L` z*GNfHhoJmuU0>p3R(1x(6RCzM}>cA=|{a;SQisf3DRJ*V1HxAugt;SN}Dq5AfP9Ef+bNW;^se7q7 zmz%n|Ma|o%#JF^>WoLvBK#@^)_K9T)dE2BWbNnMak8wOxC64)QSEpc(9G>Le$@wpu zZQ!|E$wGgI(`t9i&!8WjHEX82(`j=3N>0@%cxn)(Nt}vzrZ16cV7uD}qT%ve4y

  • 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 literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/renderchami.jpg b/plugin/card_game/resources/css/renderchami.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54e697fa05a17485cf697a90d3e49aa45b17abcb GIT binary patch literal 28026 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 literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/css/scissorsright.gif b/plugin/card_game/resources/css/scissorsright.gif new file mode 100644 index 0000000000000000000000000000000000000000..e0758d0065d969fe3c4a1e2102d4e9354a866460 GIT binary patch literal 5974 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 literal 0 HcmV?d00001 diff --git a/plugin/card_game/resources/js/cardgame.js b/plugin/card_game/resources/js/cardgame.js new file mode 100644 index 000000000..843c6ef4d --- /dev/null +++ b/plugin/card_game/resources/js/cardgame.js @@ -0,0 +1,237 @@ +/* For license terms, see /license.txt */ +/** + * Whenever the cardgame.js file is included into the loaded JavaScript, + * search for the user picture in the left menu and add a div + * "enjoy-cardgame" to it to show a little drawing cards icon to enable + * this plugin + */ +$(document).ready(function ($) { + + if (!document.getElementById('havedeckcardgame')) { + $('.sidebar .panel-body .img-circle').parent().parent().after('

    ') + } else { + $('.sidebar .panel-body .img-circle').parent().parent().after('
    ') + } +}) + +/** + * This function inserts the
    element 'home-card' for the card game + * before the .page-content element on the page (the right side of the + * "my courses" page) only if the element with id 'home-card' does not exist + * yet. + */ +function installCardView () { + if (!document.getElementById('home-card')) { + var mess1 = $('#cardgamemessage').html() + var h = '
    ' + h += '
    ' + var panClass = 'pimg0' + $('#pancardgame').html() + + // Setup the 3x5 table of images + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + + h += '
    ' + h += '
    ' + h += '
    ' + h += '
    ' + + h += '
    ' + h += '
    ' + + h += '
    ' + h += '
    ' + mess1 + '
    ' + + h += '
    ' + h += '
    ' + h += '
    ' + + $('.page-content').before(h) + + } else { + $('#home-card').fadeToggle() + } + +} + +/** + * Animate the scissors + */ +function animationOpenCardView () { + + $('#scissors').addClass('scissorsrightinitfinal') + + setTimeout(function () { + $('#scissors').fadeOut() + $('.linescissors').fadeOut() + $('#messagePackDeck').css('display', 'none') + $('#messagePackDeck').removeClass('messagePackDeck') + + }, 2600) + + setTimeout(function () { + $('.cardgame-pack').addClass('cardgame-open') + $('.card-one').addClass('card-one-2') + }, 3000) + +} + +function cardOpenCardView () { + + $('.card-one').removeClass('card-one-2') + + setTimeout(function () { + $('.puzzlecardone').fadeIn() + randomOpenCardView() + }, 700) + +} + +function randomOpenCardView () { + + var part = Math.floor(Math.random() * (15 - 1 + 1)) + 1 + + $('.puzzlecardone').css('display', 'block') + $('#scissors').fadeOut() + $('.linescissors').fadeOut() + $('#messagePackDeck').css('display', 'none') + $('#messagePackDeck').removeClass('messagePackDeck') + + var memocardgame = $('#memocardgame').html() + var parts = memocardgame + var arrayOfStrings = parts.split(';') + + var findDouble = false + for (var i = 0; i < arrayOfStrings.length; i++) { + var idpart = arrayOfStrings[i] + idpart = idpart.replace('!!', '') + idpart = idpart.replace('!', '') + idpart = idpart.replace('!', '') + idpart = idpart.replace(';', '') + $('#puzzlepart' + idpart).css('opacity', 0) + + if (part == idpart) { + findDouble = true + } + + } + if (findDouble) { + part = Math.floor(Math.random() * (15 - 1 + 1)) + 1 + } + if (memocardgame.indexOf('!' + part + ';') != -1) { + part = Math.floor(Math.random() * (15 - 1 + 1)) + 1 + } + if (memocardgame.indexOf('!' + part + ';') != -1) { + part = Math.floor(Math.random() * (15 - 1 + 1)) + 1 + } + + if (memocardgame.indexOf('!' + part + ';') != -1) { + var mess2 = $('#cardgameloose').html() + $('#messagePackDeck').html(mess2) + $('#messagePackDeck').css('display', 'block') + $('#messagePackDeck').css('backgroundColor', 'red') + $('#messagePackDeck').addClass('messagePackDeckBottom') + var lk = $('#linkcardgame').html() + $.ajax({ url: lk + '?loose=1' }).done(function () { }) + + } else { + var lk = $('#linkcardgame').html() + $.ajax({ url: lk + '?part=' + part }).done(function () { }) + + setTimeout(function () { + $('#puzzlepart' + part).addClass('puzzlepartstar') + }, 600) + + setTimeout(function () { + $('#puzzlepart' + part).css('opacity', 0) + }, 1200) + + setTimeout(function () { + var mess2 = $('#cardgameengage').html() + $('#messagePackDeck').html(mess2) + $('#messagePackDeck').css('display', 'block') + $('#messagePackDeck').addClass('messagePackDeckBottom') + $('#viewDeckBottom').css('display', 'block') + }, 1500) + + } + $('#plugin-cardgame-icon').removeClass('enjoy-cardgame-active') + $('#plugin-cardgame-icon').addClass('enjoy-cardgame') +} + +/** + * This function adds the cardgame area block (in invisible state), then + * changes the visibility of blocks inside the puzzle area to show them + */ +function onlyOpenCardView () { + + installCardView(); + + var memocardgame = $('#memocardgame').html() + + $('.puzzlecardone').css('display', 'block') + $('#scissors').css('display', 'none') + $('.linescissors').css('display', 'none') + $('#messagePackDeck').css('display', 'none') + $('#messagePackDeck').removeClass('messagePackDeck') + + var lk = $('#linkcardgame').html() + var parts = memocardgame + // alert(parts); + var arrayOfStrings = parts.split(';') + + for (var i = 0; i < arrayOfStrings.length; i++) { + var idpart = arrayOfStrings[i] + idpart = idpart.replace('!!', '') + idpart = idpart.replace('!', '') + idpart = idpart.replace('!', '') + idpart = idpart.replace(';', '') + $('#puzzlepart' + idpart).css('opacity', 0) + } + + var mess2 = $('#cardgameengage').html() + $('#messagePackDeck').html(mess2) + $('#messagePackDeck').css('display', 'block') + $('#messagePackDeck').addClass('messagePackDeckBottom') + $('#viewDeckBottom').css('display', 'block') +} + +function minimizePuzzle () { + + $('.puzzlecardone').addClass('puzzleMin') + setTimeout(function () { + + $('.puzzleMinOther').css('display', 'block') + + var panNumber = parseInt($('#pancardgame').html()) + + if (panNumber > 1) { + $('#puzzleMinOther1').addClass('pimg01') + } + if (panNumber > 2) { + $('#puzzleMinOther2').addClass('pimg02') + } + if (panNumber > 3) { + $('#puzzleMinOther3').addClass('pimg03') + } + if (panNumber > 4) { + $('#puzzleMinOther4').addClass('pimg04') + } + }, 1000) +} + diff --git a/plugin/card_game/uninstall.php b/plugin/card_game/uninstall.php new file mode 100644 index 000000000..5d0bc768e --- /dev/null +++ b/plugin/card_game/uninstall.php @@ -0,0 +1,9 @@ +uninstall(); diff --git a/plugin/check_extra_field_author_company/CheckExtraFieldAuthorsCompanyPlugin.php b/plugin/check_extra_field_author_company/CheckExtraFieldAuthorsCompanyPlugin.php new file mode 100644 index 000000000..3ed6cbb77 --- /dev/null +++ b/plugin/check_extra_field_author_company/CheckExtraFieldAuthorsCompanyPlugin.php @@ -0,0 +1,553 @@ +tblExtraField = Database::get_main_table(TABLE_EXTRA_FIELD); + $this->tblExtraFieldOption = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS); + $field = new ExtraField('user'); + $companyField = $field->get_handler_field_info_by_field_variable('company'); + $this->companyExists = false; + if (!empty($companyField)) { + $this->companyExists = true; + $this->companyField = $companyField; + } else { + $this->companyField = [ + 'field_type' => ExtraField::FIELD_TYPE_RADIO, + 'variable' => 'company', + 'display_text' => 'Company', + 'default_value' => '', + 'field_order' => 0, + 'visible_to_self' => 0, + 'visible_to_others' => 0, + 'changeable' => 1, + 'filter' => 1, + ]; + } + $field = new ExtraField('lp'); + $authorsField = $field->get_handler_field_info_by_field_variable('authors'); + + $this->authorsExists = false; + if (empty($authorsField)) { + $this->authorsExists = true; + $this->authorsField = $authorsField; + } + } + + /** + * Create a new instance of CheckExtraFieldAuthorsCompanyPlugin. + * + * @return CheckExtraFieldAuthorsCompanyPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Perform the plugin installation. + */ + public function install() + { + $this->saveCompanyField(); + $this->setCompanyExtrafieldData(); + $this->saveAuthorsField(); + $this->savePrice(); + $this->saveAuthorLPItem(); + $this->saveAuthorLp(); + } + + /** + * Save the arrangement for company, it is adjusted internally so that the values match the necessary ones. + */ + public function saveCompanyField() + { + $data = $this->companyField; + if (!empty($data)) { + $data['field_type'] = (int) $data['field_type']; + $data['field_order'] = (int) $data['field_order']; + $data['visible_to_self'] = (int) $data['visible_to_self']; + $data['visible_to_others'] = (int) $data['visible_to_others']; + $data['changeable'] = (int) $data['changeable']; + $data['filter'] = (int) $data['filter']; + $data['default_value'] = ''; + $data['variable'] = 'company'; + $data['visible'] = 1; + $data['display_text'] = strtolower(Database::escape_string($data['display_text'])); + $schedule = new ExtraField('user'); + $this->companyField['id'] = $schedule->save($data); + } + } + + /** + * Insert the option fields for company with the generic values Company 1, company 2 and company 3. + */ + public function setCompanyExtrafieldData() + { + $companies = [ + 0 => 'Company 1', + 1 => 'Company 2', + 2 => 'Company 3', + ]; + $companyId = (int) $this->companyField['id']; + if ($companyId != 0) { + for ($i = 0; $i < count($companies); $i++) { + $order = $i + 1; + $extraFieldOptionValue = $companies[$i]; + if ($companyId != null) { + $query = "SELECT * + FROM ".$this->tblExtraFieldOption." + WHERE + option_value = '$extraFieldOptionValue' AND + field_id = $companyId"; + $extraFieldOption = Database::fetch_assoc(Database::query($query)); + if (isset($extraFieldOption['id']) && $extraFieldOption['id'] && $extraFieldOption['field_id'] == $companyId) { + // Update? + } else { + $query = " + INSERT INTO ".$this->tblExtraFieldOption." + (`field_id`, `option_value`, `display_text`, `priority`, `priority_message`, `option_order`) VALUES + ( '$companyId', '$extraFieldOptionValue', '$extraFieldOptionValue', NULL, NULL, '$order'); + "; + Database::query($query); + } + } + } + } + } + + /** + * Save the arrangement for authors, it is adjusted internally so that the values match the necessary ones. + */ + public function saveAuthorsField() + { + $data = [ + 'field_type' => ExtraField::FIELD_TYPE_SELECT_MULTIPLE, + 'variable' => 'authors', + 'display_text' => 'Authors', + 'default_value' => '', + 'field_order' => 0, + 'visible_to_self' => 1, + 'visible_to_others' => 0, + 'changeable' => 1, + 'filter' => 1, + ]; + $schedule = new ExtraField('lp'); + $schedule->save($data); + } + + /** + * Save the arrangement for price, it is adjusted internally so that the values match the necessary ones. + */ + public function savePrice() + { + $schedule = new ExtraField('lp_item'); + $data = []; + $data['visible_to_self'] = 1; + $data['visible_to_others'] = 1; + $data['changeable'] = 1; + $data['filter'] = 0; + $data['variable'] = 'price'; + $data['display_text'] = 'SalePrice'; + $data['field_type'] = ExtraField::FIELD_TYPE_INTEGER; + + $schedule->save($data); + } + + /** + * Save the arrangement for AuthorLPItem, it is adjusted internally so that the values match the necessary ones. + */ + public function saveAuthorLPItem() + { + $schedule = new ExtraField('lp_item'); + $data = []; + $data['visible_to_self'] = 1; + $data['visible_to_others'] = 0; + $data['changeable'] = 1; + $data['filter'] = 0; + $data['variable'] = 'authorlpitem'; + $data['display_text'] = 'LearningPathItemByAuthor'; + $data['field_type'] = ExtraField::FIELD_TYPE_SELECT_MULTIPLE; + $schedule->save($data); + } + + /** + * Save the arrangement for authorlp, it is adjusted internally so that the values match the necessary ones. + */ + public function saveAuthorLp() + { + $schedule = new ExtraField('user'); + $data = []; + $data['variable'] = 'authorlp'; + $data['display_text'] = 'authors'; + $data['changeable'] = 1; + $data['visible_to_self'] = 1; + $data['visible_to_others'] = 0; + $data['filter'] = 0; + $data['field_type'] = ExtraField::FIELD_TYPE_CHECKBOX; + $schedule->save($data); + } + + /** + * Remove the extra fields set by the plugin. + */ + public function uninstall() + { + $companyExists = $this->companyFieldExists(); + if ($companyExists == true) { + // $this->removeCompanyField(); + } + $authorsExists = $this->authorsFieldExists(); + if ($authorsExists == true) { + // $this->removeAuthorsField(); + } + $priceExists = $this->priceFieldExists(); + if ($priceExists == true) { + // $this->>removePriceField(); + } + $authorLpItemExists = $this->authorLpItemFieldExists(); + if ($authorLpItemExists == true) { + // $this->removeAuthorLpItemField(); + } + $authorLpExists = $this->authorLpFieldExists(); + if ($authorLpExists == true) { + // $this->removeAuthorLpField(); + } + } + + /** + * Verify that the "company" field exists in the database. + */ + public function companyFieldExists(): bool + { + $this->getCompanyField(); + $this->companyExists = (isset($this->companyField['id'])) ? true : false; + + return $this->companyExists; + } + + /** + * Returns the content of the extra field "company" if it exists in the database, if not, it returns an arrangement + * with the basic elements for its operation. + * + * @return array + */ + public function getCompanyField() + { + $companyField = $this->getInfoExtrafield('company'); + if (count($companyField) > 1) { + $this->companyField = $companyField; + } else { + $companyField = $this->companyField; + } + + return $companyField; + } + + /** + * Verify that the "authors" field exists in the database. + */ + public function authorsFieldExists(): bool + { + $this->getAuthorsField(); + $this->authorsExists = (isset($this->authorsField['id'])) ? true : false; + + return $this->authorsExists; + } + + /** + * Returns the content of the extra field "authors" if it exists in the database, if not, it returns an arrangement + * with the basic elements for its operation. + * + * @return array + */ + public function getAuthorsField() + { + $schedule = new ExtraField('lp'); + $data = $schedule->get_handler_field_info_by_field_variable('authors'); + if (empty($data)) { + $this->authorsField = $data; + } else { + $data = $this->authorsField; + } + + return $data; + } + + /** + * Verify that the "price" field exists in the database. + */ + public function priceFieldExists(): bool + { + $this->getPriceField(); + $this->priceExists = (isset($this->priceField['id'])) ? true : false; + + return $this->priceExists; + } + + /** + * Returns the content of the extra field "price" if it exists in the database, if not, it returns an arrangement + * with the basic elements for its operation. + * + * @return array + */ + public function getPriceField() + { + $schedule = new ExtraField('lp_item'); + $data = $schedule->get_handler_field_info_by_field_variable('price'); + if (empty($data)) { + $this->priceField = $data; + } else { + $data = $this->priceField; + } + + return $data; + } + + /** + * Verify that the "authorlpitem" field exists in the database. + */ + public function authorLpItemFieldExists(): bool + { + $this->getAuthorLpItemField(); + $this->authorLpItemExists = (isset($this->authorLpItemField['id'])) ? true : false; + + return $this->authorLpItemExists; + } + + /** + * Returns the content of the extra field "authorlpitem" if it exists in the database, if not, it returns an arrangement + * with the basic elements for its operation. + * + * @return array + */ + public function getAuthorLpItemField() + { + $schedule = new ExtraField('lp_item'); + $data = $schedule->get_handler_field_info_by_field_variable('authorlpitem'); + if (empty($data)) { + $this->authorLpItemField = $data; + } else { + $data = $this->authorLpItemField; + } + + return $data; + } + + /** + * Verify that the "authorlp" field exists in the database. + */ + public function authorLpFieldExists(): bool + { + $this->getAuthorLpField(); + $this->authorLpExists = (isset($this->authorLpField['id'])) ? true : false; + + return $this->authorLpExists; + } + + /** + * Returns the content of the extra field "authorlp" if it exists in the database, if not, it returns an arrangement + * with the basic elements for its operation. + * + * @return array + */ + public function getAuthorLpField() + { + $field = new ExtraField('user'); + $data = $field->get_handler_field_info_by_field_variable('authorlp'); + if (empty($data)) { + $this->authorLpField = $data; + } else { + $data = $this->authorLpField; + } + + return $data; + } + + /** + * Remove the extra fields "company". + */ + public function removeCompanyField() + { + $data = $this->getCompanyField(); + // $this->deleteQuery($data); + } + + /** + * Remove the extra fields "authors". + */ + public function removeAuthorsField() + { + $data = $this->getAuthorsField(); + // $this->deleteQuery($data); + } + + /** + * Remove the extra fields "price". + */ + public function removePriceField() + { + $data = $this->getPriceField(); + // $this->deleteQuery($data); + } + + /** + * Remove the extra fields "authorlpitem". + */ + public function removeAuthorLpItemField() + { + $data = $this->getAuthorLpItemField(); + // $this->deleteQuery($data); + } + + /** + * Remove the extra fields "authorlp". + */ + public function removeAuthorLpField() + { + $data = $this->getAuthorLpField(); + // $this->deleteQuery($data); + } + + /** + * Executes fix removal for authors or company. + * + * @param $data + */ + protected function deleteQuery($data) + { + $validVariable = false; + $variable = $data['variable']; + $extraFieldTypeInt = (int) $data['extra_field_type']; + $FieldType = (int) $data['field_type']; + $id = (int) $data['id']; + $extraFieldType = null; + switch ($variable) { + case 'company': + case 'authorlp': + $validVariable = true; + $extraFieldType = 'user'; + break; + case 'authors': + $validVariable = true; + $extraFieldType = 'lp'; + break; + case 'price': + case 'authorlpitem': + $validVariable = true; + $extraFieldType = 'lp_item'; + break; + } + if ($variable === 'company') { + } elseif ($variable === 'authors') { + } + if ($validVariable == true && $id != 0 && !empty($extraFieldType)) { + $query = "SELECT id + FROM + ".$this->tblExtraField." + WHERE + id = $id AND + variable = '$variable' AND + extra_field_type = $extraFieldTypeInt AND + field_type = $FieldType + "; + $data = Database::fetch_assoc(Database::query($query)); + if (isset($data['id'])) { + $obj = new ExtraField($extraFieldType); + $obj->delete($data['id']); + } + } + } + + /** + * Returns the array of an element in the database that matches the variable. + * + * @param string $variableName + * + * @return array + */ + protected function getInfoExtrafield($variableName = null) + { + if ($variableName == null) { + return []; + } + $variableName = strtolower(Database::escape_string($variableName)); + $tblExtraField = $this->tblExtraField; + $query = "SELECT * FROM $tblExtraField WHERE variable = '$variableName'"; + $data = Database::fetch_assoc(Database::query($query)); + if ($data == false || !isset($data['display_text'])) { + return []; + } + + return $data; + } +} diff --git a/plugin/check_extra_field_author_company/README.md b/plugin/check_extra_field_author_company/README.md new file mode 100644 index 000000000..0e58972f0 --- /dev/null +++ b/plugin/check_extra_field_author_company/README.md @@ -0,0 +1,30 @@ +Check Extra Fields 'author' and 'company' +====== + +The "User by organization" report allows the administrator to select a +date range to show the number of users who have been subscribed to a learning +path or a course during this time frame. The number of users are grouped by +entity/company. + +The "Learning path by author" report allows the administrator to define, for +each user, if (s)he is an author or not. Then, for each item in a Learning +Path, the administrator can select who is its author from the identified list +and indicate the cost of that item. + +Finally, the reports allow the administrator to select a date range to show +for each author how many of his/her content (LP item) users have been given +access to (based on the learning path subscriptions by users) and show the +amount of money they should be paid based on the number of accesses given +during this period. + +This plugin adds the extra fields necessary to display the reports: + +* The "User by organization" report requires the 'company' extra field to be created on user. +* The "Learning path by author" report requires the 'authors' extra field to be created on lp. +* The "LP Item by author" report additional reports requires the 'authorlpitem' extra field to be created on lp_item and the 'authorlp' extra field to be created on 'user'. +* For prices to be adequately shown, the 'price' extra field needs to be created on 'lp_item'. + +## Uninstall + +When uninstalling this plugin, the extra fields created will not be removed, +for data persistence reasons. \ No newline at end of file diff --git a/plugin/check_extra_field_author_company/index.php b/plugin/check_extra_field_author_company/index.php new file mode 100644 index 000000000..92a1ce705 --- /dev/null +++ b/plugin/check_extra_field_author_company/index.php @@ -0,0 +1,5 @@ +install(); diff --git a/plugin/check_extra_field_author_company/plugin.php b/plugin/check_extra_field_author_company/plugin.php new file mode 100644 index 000000000..9f110c2fa --- /dev/null +++ b/plugin/check_extra_field_author_company/plugin.php @@ -0,0 +1,17 @@ +Plugins). + * + * @author Carlos Alvarado + */ +$plugin_info['title'] = 'Extra reports: User by organization", "Learning path by author" and "LP item by author"'; +$plugin_info['comment'] = 'To enable these reports, enable user subscription to a learning path through the '. + '"Learning path settings" -> "Subscribe users to learning path"
    '. + 'You can then go to /main/mySpace/ (as administrator), then to the "admin" section (star icon) to see the new reports.'; +$plugin_info['version'] = '1.2'; +$plugin_info['author'] = 'Carlos Alvarado, Julio Montoya'; diff --git a/plugin/check_extra_field_author_company/uninstall.php b/plugin/check_extra_field_author_company/uninstall.php new file mode 100644 index 000000000..13d8d6e56 --- /dev/null +++ b/plugin/check_extra_field_author_company/uninstall.php @@ -0,0 +1,8 @@ +uninstall(); diff --git a/plugin/cleandeletedfiles/README.md b/plugin/cleandeletedfiles/README.md new file mode 100644 index 000000000..781494fda --- /dev/null +++ b/plugin/cleandeletedfiles/README.md @@ -0,0 +1,6 @@ +CleanDeleted Files Maintenance plugin +=== + +This plugin allows the administrator to permanently delete files marked as deleted. +Enable the plugin, then add it to the menu_administrator region and check back the +'Plugins' box on the administration page to access it. \ No newline at end of file diff --git a/plugin/cleandeletedfiles/admin.php b/plugin/cleandeletedfiles/admin.php new file mode 100644 index 000000000..374f59fae --- /dev/null +++ b/plugin/cleandeletedfiles/admin.php @@ -0,0 +1,192 @@ +get_info(); +$isPlatformAdmin = api_is_platform_admin(); + +if ($plugin->isEnabled() && $isPlatformAdmin) { + $htmlHeadXtra[] = ''; + + $nameTools = $plugin->get_lang("FileList"); + Display::display_header($nameTools); + echo Display::page_header($nameTools); + + $pathList = [ + "app/courses", + "app/upload", + ]; + + function findDeletedFiles($pathRelative) + { + global $sizePath; + $pathAbsolute = api_get_path(SYS_PATH).$pathRelative; + $result = []; + if (is_dir($pathAbsolute)) { + $dir = dir($pathAbsolute); + while ($file = $dir->read()) { + if (is_file($pathAbsolute.'/'.$file)) { + $filesize = round(filesize($pathAbsolute.'/'.$file) / 1024, 1); + $pos = strpos($file, "DELETED"); + if ($pos !== false) { + $result[] = [ + 'path_complete' => $pathAbsolute.'/'.$file, + 'path_relative' => $pathRelative.'/'.$file, + 'size' => $filesize, + ]; + $sizePath += $filesize; + } + } else { + if ($file != '..' && $file != '.') { + $result = array_merge($result, findDeletedFiles($pathRelative.'/'.$file)); + } + } + } + } + + return $result; + } + + $sizeTotal = 0; + $i = 0; + foreach ($pathList as $pathItem) { + $sizePath = 0; + $filesDeletedList = findDeletedFiles($pathItem); + echo Display::page_subheader($plugin->get_lang("path_dir").": ".$pathItem); + + if (count($filesDeletedList) > 0) { + echo "
      "; + echo "
    • ".$plugin->get_lang('FilesDeletedMark').": ".count($filesDeletedList).""; + echo "
    • ".$plugin->get_lang('FileDirSize').": "; + if ($sizePath >= 1024) { + echo "".round($sizePath / 1024, 1)." Mb"; + } else { + echo "".$sizePath." Kb"; + } + echo "
    "; + + $header = [ + [ + '', + false, + null, + ['style' => 'text-align:center'], + ], + [$plugin->get_lang('path_dir'), true], + [$plugin->get_lang('size'), true, null, ['style' => 'min-width:85px']], + [get_lang('Actions'), false], + ]; + + $data = []; + $deleteIcon = Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL); + + foreach ($filesDeletedList as $value) { + $tools = Display::url( + $deleteIcon, + 'file://'.$value['path_complete'], + ['class' => 'delete-file'] + ); + + $row = [ + '', + $value['path_relative'], + $value['size'].' '.($value['size'] >= 1024 ? 'Mb' : 'Kb'), + $tools, + ]; + $data[] = $row; + } + + echo Display::return_sortable_table( + $header, + $data, + [], + ['per_page' => 100], + [] + ); + } else { + $message = $plugin->get_lang('NoFilesDeleted'); + echo Display::return_message($message, 'warning', false); + } + $sizeTotal += $sizePath; + echo '
    '; + $i++; + } + + if ($sizeTotal >= 1024) { + echo $plugin->get_lang('SizeTotalAllDir').": ".round($sizeTotal / 1024, 1).' Mb'; + } else { + echo $plugin->get_lang('SizeTotalAllDir').": ".$sizeTotal.' Kb'; + } + echo '
    '; + echo '
    '. + $plugin->get_lang("DeleteSelectedFiles"). + ''; + + Display::display_footer(); +} diff --git a/plugin/cleandeletedfiles/config.php b/plugin/cleandeletedfiles/config.php new file mode 100644 index 000000000..0d0df917d --- /dev/null +++ b/plugin/cleandeletedfiles/config.php @@ -0,0 +1,10 @@ +get_info(); diff --git a/plugin/cleandeletedfiles/src/CleanDeletedFilesPlugin.php b/plugin/cleandeletedfiles/src/CleanDeletedFilesPlugin.php new file mode 100644 index 000000000..2c9df77f9 --- /dev/null +++ b/plugin/cleandeletedfiles/src/CleanDeletedFilesPlugin.php @@ -0,0 +1,34 @@ + 'boolean']); + $this->isAdminPlugin = true; + } + + /** + * @return RedirectionPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } +} diff --git a/plugin/cleandeletedfiles/src/ajax.php b/plugin/cleandeletedfiles/src/ajax.php new file mode 100644 index 000000000..0ede69d3d --- /dev/null +++ b/plugin/cleandeletedfiles/src/ajax.php @@ -0,0 +1,47 @@ + "false", "message" => $plugin->get_lang('ErrorEmptyPath')]); + exit; + } + + if (unlink($path)) { + Display::addFlash($plugin->get_lang("DeletedSuccess"), 'success'); + echo json_encode(["status" => "true"]); + } else { + echo json_encode(["status" => "false", "message" => $plugin->get_lang('ErrorDeleteFile')]); + } + break; + case 'delete-files-list': + $list = isset($_REQUEST['list']) ? $_REQUEST['list'] : []; + if (empty($list)) { + echo json_encode(["status" => "false", "message" => $plugin->get_lang('ErrorEmptyPath')]); + exit; + } + + foreach ($list as $value) { + if (empty($value)) { + continue; + } + unlink($value); + } + + Display::addFlash($plugin->get_lang("DeletedSuccess"), 'success'); + echo json_encode(["status" => "true"]); + break; +} diff --git a/plugin/clockworksms/README.md b/plugin/clockworksms/README.md new file mode 100644 index 000000000..7c9138b3a --- /dev/null +++ b/plugin/clockworksms/README.md @@ -0,0 +1,6 @@ +Clockworks SMS plugin +=== + +This plugin enables Chamilo to send SMS messages to Chamilo users when a notification email is sent. + +SMS message types (a new course is created, an account is approved and so on) can be enabled or disabled from it's plugin configuration option. \ No newline at end of file diff --git a/plugin/clockworksms/config.php b/plugin/clockworksms/config.php new file mode 100644 index 000000000..a736ea8c1 --- /dev/null +++ b/plugin/clockworksms/config.php @@ -0,0 +1,15 @@ + + */ +require_once __DIR__.'/../../main/inc/global.inc.php'; + +require_once 'lib/clockworksms.lib.php'; +require_once 'vendor/clockworksms_api.php'; +require_once 'lib/clockworksms_plugin.class.php'; diff --git a/plugin/clockworksms/index.php b/plugin/clockworksms/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/clockworksms/index.php @@ -0,0 +1 @@ + + */ +require_once __DIR__.'/config.php'; +ClockworksmsPlugin::create()->install(); diff --git a/plugin/clockworksms/lang/english.php b/plugin/clockworksms/lang/english.php new file mode 100644 index 000000000..3f11465d4 --- /dev/null +++ b/plugin/clockworksms/lang/english.php @@ -0,0 +1,102 @@ +Click here to get it."; + +$strings['tool_enable'] = "Enable SMS sending"; +$strings['tool_enable_help'] = "Choose if you want to enable SMS sending tool. + Once enabled, they will be sent along with an email the selected SMS message types to the users who had filled their mobile phone number field in their profile
    "; + +$strings['type'] = "Send SMS when"; +$strings['mobile_phone_number'] = "Mobile Phone (include country dialing code only)"; + +$strings['MessageWelcomeXLoginXPasswordX'] = "A user is added to the platform"; +$strings['MessageXNewFileSharedCourseXByX'] = "A file is added to dropbox"; +$strings['MessageXAccountApprovedConnectX'] = "An account is approved"; +$strings['MessageXNewCourseXBeenCreatedX'] = "A new course is created"; +$strings['MessageXNewUserXSubscribedCourseX'] = "A user is subscribed to a course"; +$strings['MessageXNewCourseSuggestedTeacherX'] = "A new course is suggested"; +$strings['MessageXCourseOpeningRequestCodeXRegistered'] = "A new course opening request is registered"; +$strings['MessageXCourseOpeningRequestCourseCodeXApproved'] = "A new course opening request is approved"; +$strings['MessageXRequestOpenCourseCodeXReject'] = "A new course opening request is rejected"; +$strings['MessageXCourseOpeningRequestCourseCodeX'] = "Additional information is required for a new course opening request"; +$strings['MessageXBeenSubscribedCourseX'] = "A user is added to the course but not registered to it yet"; +$strings['MessageXAssignmentBeenCreatedCourseX'] = "Homework is created"; +$strings['MessageXAccountCreatedUpdatedLoginXPasswordX'] = "A user account is created or updated"; +$strings['MessageXPasswordUpdatedLoginXPasswordX'] = "A user password is updated"; +$strings['MessageXRequestedPasswordChange'] = "A user requests a password change"; +$strings['MessageXReceivedNewPersonalMessages'] = "New personal messages are received in user's inbox or forum"; +$strings['MessageXNewUserXPendingApproval'] = "A new user registration is pending for approval"; +$strings['MessageXXPostedForumXCourseX'] = "A user posts in forum"; +$strings['MessageXXXCheckEmailConnectMoreInfo'] = "An announcement is sent"; +$strings['MessageXXStudentXAnsweredTestX'] = "A student solves an exercise"; +$strings['MessageXXStudentXAnsweredTestXOpenQuestion'] = "A student solves an open question"; +$strings['MessageXXStudentXAnsweredTestXVoiceQuestion'] = "A student solves a voice question"; +$strings['MessageXXAnswerOpenQuestionTestXReviewed'] = "An open question answer is reviewed"; +$strings['MessageXXNewThreadXStartedForumX'] = "A new thread is started in forum"; +$strings['MessageXXNewAnswerPostedXForumX'] = "A new answer is posted in forum"; +$strings['MessageXXNewSystemAnnouncementAdded'] = "A new system announcement is added and the user does not want to send a test email"; +$strings['MessageXTestXNewSystemAnnouncementAdded'] = "A new system announcement is added and the user wants to send a test email"; +$strings['MessageXXSystemAnnouncementUpdate'] = "A new system announcement is updated and the user does not want to send a test email"; +$strings['MessageXTestXSystemAnnouncementUpdate'] = "A new system announcement is updated and the user wants to send a test email"; +$strings['MessageXUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "An assignment is uploaded and there is no current session (this will be sent only to coaches)"; +$strings['MessageXUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "An assignment is uploaded (this will be sent only to teachers)"; +$strings['MessageXUserXUploadedAssignmentXCourseX'] = "... and there is no current session (this will be sent only to coaches)"; +$strings['MessageXUserXUploadedAssignmentXCheckX'] = "... (this will be sent only to teachers)"; +$strings['MessageXSubscribedSessionX'] = "A session is created or updated and new users are subscribed"; +$strings['MessageXSubscribedSessionXCSV'] = "New users are subscribed to a session importing a CSV file"; +$strings['MessageXUserXSuggestedBeFriends'] = "A friendship invitation is received"; +$strings['MessageXUserXAnsweredInboxMessage'] = "A message reply is received in inbox"; +$strings['MessageXBeenInvitedJoinGroupX'] = "A group join invitation is received"; +$strings['MessageXMessagesSentEditedGroupXEdited'] = "A group topic message is updated"; +$strings['MessageXMessagesSentEditedGroupXAdded'] = "A group topic message is added"; +$strings['MessageXBeenInvitedCompleteSurveyXCourseX'] = "A new survey is created"; +$strings['MessageXReminderAssignmentXCourseXDue'] = "A reminder is sent to students who have not uploaded their assignment yet"; +$strings['MessageXUserDetailsModified'] = "User details have been updated"; + +$strings['WelcomeXLoginXPasswordX'] = "Welcome to %s. Here are your user account details: login: %s, password: %s"; +$strings['XNewFileSharedCourseXByX'] = "%s: A new file has been shared with you in course %s by %s"; +$strings['XAccountApprovedConnectX'] = "%s: Your account has been approved by an administrator. You can now connect to %s"; +$strings['XNewCourseXBeenCreatedX'] = "%s: A new course (%s) has been created by %s"; +$strings['XNewUserXSubscribedCourseX'] = "%s: A new user (%s) has just subscribed to your course (%s)"; +$strings['XNewCourseSuggestedTeacherX'] = "%s: A new course has been suggested by teacher (%s). Please connect to the admin page to review it"; +$strings['XCourseOpeningRequestCodeXRegistered'] = "%s: Your course opening request (code %s) has been registered. You will be informed when it is approved"; +$strings['XCourseOpeningRequestCourseCodeXApproved'] = "%s: Your course opening request for course code %s has been approved. Connect to your portal to use it"; +$strings['XRequestOpenCourseCodeXReject'] = "%s: We are sorry to inform you that your request to open course code %s has been rejected"; +$strings['XCourseOpeningRequestCourseCodeX'] = "%s: Your course opening request for course code %s requires more information. Check e-mail for more info"; +$strings['XBeenSubscribedCourseX'] = "%s: You have been subscribed to course %s"; +$strings['XAssignmentBeenCreatedCourseX'] = "%s: A new assignment has been created in course %s. Please connect for more info"; +$strings['XAccountCreatedUpdatedLoginXPasswordX'] = "%s: Your account has been created or updated. You can now login as %s, password: %s"; +$strings['XPasswordUpdatedLoginXPasswordX'] = "%s: Your password has been updated. You can now login as %s, password: %s"; +$strings['XRequestedPasswordChange'] = "%s: You have requested a password change. Please check your e-mail for more information"; +$strings['XReceivedNewPersonalMessages'] = "%s: You have received new personal messages in your inbox or in the forum. Please check your e-mail for more info"; +$strings['XNewUserXPendingApproval'] = "%s: A new user (%s) registration is pending your approval. Please connect to the admin panel to review"; +$strings['XXPostedForumXCourseX'] = "%s: %s just posted on forum %s in course %s"; +$strings['XXXCheckEmailConnectMoreInfo'] = "%s: %s: %s. Check your e-mail or connect for more info"; +$strings['XXStudentXAnsweredTestX'] = "%s: %s: Student %s has just answered test %s"; +$strings['XXStudentXAnsweredTestXOpenQuestion'] = "%s: %s: Student %s answered test %s's open question. Please connect to review"; +$strings['XXStudentXAnsweredTestXVoiceQuestion'] = "%s: %s: Student %s answered test %s's voice question. Please connect to review"; +$strings['XXAnswerOpenQuestionTestXReviewed'] = "%s: %s: Your answer to an open question in test %s has been reviewed"; +$strings['XXNewThreadXStartedForumX'] = "%s: %s: A new thread (%s) has been started in forum %s"; +$strings['XXNewAnswerPostedXForumX'] = "%s: %s: A new answer has been posted to (%s) in forum %s"; +$strings['XXNewSystemAnnouncementAdded'] = "%s: %s"; +$strings['XTestXNewSystemAnnouncementAdded'] = "%s (test): %s"; +$strings['XXSystemAnnouncementUpdate'] = "%s: %s (update)"; +$strings['XTestXSystemAnnouncementUpdate'] = "%s (test): %s"; +$strings['XUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "%s: User %s uploaded assignment %s in course %s"; +$strings['XUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "%s: User %s uploaded assignment %s. Check %s"; +$strings['XUserXUploadedAssignmentXCourseX'] = "%s: User %s uploaded assignment %s in course %s"; +$strings['XUserXUploadedAssignmentXCheckX'] = "%s: User %s uploaded assignment %s. Check %s"; +$strings['XSubscribedSessionX'] = "%s: You have been subscribed to session %s"; +$strings['XSubscribedSessionXCSV'] = "%s: You have been subscribed to session %s"; +$strings['XUserXSuggestedBeFriends'] = "%s: User %s suggested you should be friends. Check your inbox or e-mail for details"; +$strings['XUserXAnsweredInboxMessage'] = "%s: User %s just answered your Inbox message"; +$strings['XBeenInvitedJoinGroupX'] = "%s: you have been invited to join group %s. Check your Inbox for details"; +$strings['XMessagesSentEditedGroupXEdited'] = "%s: Messages sent or edited in group %s. Check your Inbox for details"; +$strings['XMessagesSentEditedGroupXAdded'] = "%s: Messages sent or edited in group %s. Check your Inbox for details"; +$strings['XBeenInvitedCompleteSurveyXCourseX'] = "%s: You have been invited to complete survey %s in course %s"; +$strings['XReminderAssignmentXCourseXDue'] = "%s: This is a simple reminder that your assignment (%s) in course %s is still due"; +$strings['XUserDetailsModified'] = "%s: Your user details have been modified. Please check your e-mail for more information"; diff --git a/plugin/clockworksms/lang/spanish.php b/plugin/clockworksms/lang/spanish.php new file mode 100644 index 000000000..0d21fe339 --- /dev/null +++ b/plugin/clockworksms/lang/spanish.php @@ -0,0 +1,102 @@ +haga click aquí"; + +$strings['tool_enable'] = "Activar envío de SMS"; +$strings['tool_enable_help'] = "Escoja si desea activar la herramienta de envío de mensajes a celulares Clockwork SMS. + Una vez activada, se enviarán, simultaneamente con los correos, los tipos mensajes SMS seleccionados a los usuarios que hayan especificado un número de celular en su perfil.
    "; + +$strings['sms_types'] = "Enviar un SMS cuando"; +$strings['mobile_phone_number'] = "Celular (incluya sólo el código de llamada del país)"; + +$strings['MessageWelcomeXLoginXPasswordX'] = "Se añada un usuario a la plataforma"; +$strings['MessageXNewFileSharedCourseXByX'] = "Un archivo se añada a dropbox"; +$strings['MessageXAccountApprovedConnectX'] = "Se apruebe una cuenta de usuario"; +$strings['MessageXNewCourseXBeenCreatedX'] = "Se cree un curso nuevo"; +$strings['MessageXNewUserXSubscribedCourseX'] = "Un usuario se suscriba a un curso"; +$strings['MessageXNewCourseSuggestedTeacherX'] = "Un profesor sugiera un curso nuevo"; +$strings['MessageXCourseOpeningRequestCodeXRegistered'] = "Se registre una solicitud de apertura de curso"; +$strings['MessageXCourseOpeningRequestCourseCodeXApproved'] = "Se apruebe una solicitud de apertura de curso"; +$strings['MessageXRequestOpenCourseCodeXReject'] = "Se rechace una solicitud de apertura de curso"; +$strings['MessageXCourseOpeningRequestCourseCodeX'] = "Se requiera más información de una solicitud de apertura de curso"; +$strings['MessageXBeenSubscribedCourseX'] = "Se le suscriba a un curso a un usuario que aún no está registrado"; +$strings['MessageXAssignmentBeenCreatedCourseX'] = "Se cree una nueva tarea en un curso"; +$strings['MessageXAccountCreatedUpdatedLoginXPasswordX'] = "Se cree o actualice una cuenta de usuario"; +$strings['MessageXPasswordUpdatedLoginXPasswordX'] = "Se actualice la contraseña de un usuario"; +$strings['MessageXRequestedPasswordChange'] = "Un usuario solicite un cambio de contraseña"; +$strings['MessageXReceivedNewPersonalMessages'] = "Se reciban mensajes personales nuevos en la bandeja de entrada o en el foro"; +$strings['MessageXNewUserXPendingApproval'] = "Una inscripción de un nuevo usuario esté pendiente de aprobación"; +$strings['MessageXXPostedForumXCourseX'] = "Un usuario escriba en el foro"; +$strings['MessageXXXCheckEmailConnectMoreInfo'] = "Se envíe una notificación"; +$strings['MessageXXStudentXAnsweredTestX'] = "Un estudiante resuelva un ejercicio de un examen"; +$strings['MessageXXStudentXAnsweredTestXOpenQuestion'] = "Un estudiante conteste a una pregunta abierta de un examen"; +$strings['MessageXXStudentXAnsweredTestXVoiceQuestion'] = "Un estudiante conteste a una pregunta de voz de un examen"; +$strings['MessageXXAnswerOpenQuestionTestXReviewed'] = "Se revise una pregunta abierta de un examen"; +$strings['MessageXXNewThreadXStartedForumX'] = "Se cree un hilo nuevo en el foro"; +$strings['MessageXXNewAnswerPostedXForumX'] = "Se escriba una respuesta nueva en el foro"; +$strings['MessageXXNewSystemAnnouncementAdded'] = "Se añada una notificación y el usuario no desee enviar un examen por correo electrónico"; +$strings['MessageXTestXNewSystemAnnouncementAdded'] = "Se añada una notificación y el usuario desee enviar un examen por correo electrónico"; +$strings['MessageXXSystemAnnouncementUpdate'] = "Se actualice una notificación y el usuario no desee enviar un examen por correo electrónico"; +$strings['MessageXTestXSystemAnnouncementUpdate'] = "Se actualice una notificación y el usuario desee enviar un examen por correo electrónico"; +$strings['MessageXUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "Un estudiante suba su tarea y no exista una sesión (se enviará sólo a coordinadores)"; +$strings['MessageXUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "Un estudiante suba su tarea (se enviará sólo a profesores)"; +$strings['MessageXUserXUploadedAssignmentXCourseX'] = "... y no exista una sesión (se enviará sólo a coordinadores)"; +$strings['MessageXUserXUploadedAssignmentXCheckX'] = "... (se enviará sólo a profesores)"; +$strings['MessageXSubscribedSessionX'] = "Se cree o edite una sesión nueva y se suscriban a usuarios nuevos"; +$strings['MessageXSubscribedSessionXCSV'] = "Se suscriban a usuarios nuevos desde un archivo CSV"; +$strings['MessageXUserXSuggestedBeFriends'] = "Se reciba una invitación de amistad"; +$strings['MessageXUserXAnsweredInboxMessage'] = "Se reciba la contestación a un mensaje en la bandeja de entrada"; +$strings['MessageXBeenInvitedJoinGroupX'] = "Se reciba una invitación para unirse a un grupo"; +$strings['MessageXMessagesSentEditedGroupXEdited'] = "Se actualice un mensaje en un tema de grupo"; +$strings['MessageXMessagesSentEditedGroupXAdded'] = "Se añada un mensaje en un tema de grupo"; +$strings['MessageXBeenInvitedCompleteSurveyXCourseX'] = "Se cree una encuesta nueva"; +$strings['MessageXReminderAssignmentXCourseXDue'] = "Se envíe un recordatorio a los estudiantes que no hayan enviado su tarea"; +$strings['MessageXUserDetailsModified'] = "Se actualice información de un usuario"; + +$strings['WelcomeXLoginXPasswordX'] = "Bienvenido a %s. Éstos son sus datos de acceso: usuario: %s, contraseña: %s"; +$strings['XNewFileSharedCourseXByX'] = "%s: Un nuevo archivo ha sido compartido contigo en el curso %s por %s"; +$strings['XAccountApprovedConnectX'] = "%s: Su cuenta ha sido aprobada por un administrador. Ya puede conectarse a %s"; +$strings['XNewCourseXBeenCreatedX'] = "%s: Un nuevo curso (%s) ha sido creado por %s"; +$strings['XNewUserXSubscribedCourseX'] = "%s: Un nuevo usuario (%s) se ha suscrito a su curso (%s)"; +$strings['XNewCourseSuggestedTeacherX'] = "%s: Un nuevo curso ha sido sugerido por el profesor (%s). Por favor, conéctese a la página de administración para revisarlo"; +$strings['XCourseOpeningRequestCodeXRegistered'] = "%s: Su solicitud de apertura de curso (código %s) ha sido registrada. Será informado cuando ésta sea aprobada"; +$strings['XCourseOpeningRequestCourseCodeXApproved'] = "%s: Su solicitud de apertura de curso para el código de curso %s ha sido aprobada. Conéctese a su portal para usarlo"; +$strings['XRequestOpenCourseCodeXReject'] = "%s: Lamentamos informarle que su solicitud de apertura de curso para el código de curso %s ha sido rechazada"; +$strings['XCourseOpeningRequestCourseCodeX'] = "%s: Su solicitud de apertura de curso para el código de curso %s requiere más información. Compruebe su e-mail para más información"; +$strings['XBeenSubscribedCourseX'] = "%s: Ha sido suscrito al curso %s"; +$strings['XAssignmentBeenCreatedCourseX'] = "%s: Una nueva tarea ha sido creada en el curso %s. Por favor, conéctese para más información"; +$strings['XAccountCreatedUpdatedLoginXPasswordX'] = "%s: Su cuenta ha sido creada o actualizada. Ya puede conectarse como %s, contraseña: %s"; +$strings['XPasswordUpdatedLoginXPasswordX'] = "%s: Su contraseña ha sido actualizada. Ya puede conectarse como %s, contraseña: %s"; +$strings['XRequestedPasswordChange'] = "%s: Ha solicitado un cambio de contraseña. Por favor, compruebe su e-mail para más información"; +$strings['XReceivedNewPersonalMessages'] = "%s: Ha recibido mensajes personales nuevos en su bandeja de entrada o en el foro. Por favor, compruebe su e-mail para más información"; +$strings['XNewUserXPendingApproval'] = "%s: Una inscripción de un nuevo usuario (%s) está pendiente de su aprobación. Por favor, conéctese al panel de administración para revisarla"; +$strings['XXPostedForumXCourseX'] = "%s: %s acaba de escribir en el foro %s del curso %s"; +$strings['XXXCheckEmailConnectMoreInfo'] = "%s: %s: %s. Compruebe su e-mail o conéctese para más información"; +$strings['XXStudentXAnsweredTestX'] = "%s: %s: El estudiante %s acaba de contestar al test %s"; +$strings['XXStudentXAnsweredTestXOpenQuestion'] = "%s: %s: El estudiante %s contestó a la pregunta abierta del test %s. Por favor, conéctese para revisarla"; +$strings['XXStudentXAnsweredTestXVoiceQuestion'] = "%s: %s: El estudiante %s contestó a la pregunta de voz del test %s. Por favor, conéctese para revisarla"; +$strings['XXAnswerOpenQuestionTestXReviewed'] = "%s: %s: Su respuesta a la pregunta abierta en el test %s ha sido revisada."; +$strings['XXNewThreadXStartedForumX'] = "%s: %s: Se ha comenzado un nuevo hilo (%s) en el foro %s"; +$strings['XXNewAnswerPostedXForumX'] = "%s: %s: Se ha escrito una nueva respuesta a (%s) en el foro %s"; +$strings['XXNewSystemAnnouncementAdded'] = "%s: %s"; +$strings['XTestXNewSystemAnnouncementAdded'] = "%s (prueba): %s"; +$strings['XXSystemAnnouncementUpdate'] = "%s: %s (actualización)"; +$strings['XTestXSystemAnnouncementUpdate'] = "%s (prueba): %s"; +$strings['XUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "%s: El usuario %s subió la tarea %s al curso %s"; +$strings['XUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "%s: El usuario %s subió la tarea %s. Compruébelo en %s"; +$strings['XUserXUploadedAssignmentXCourseX'] = "%s: El usuario %s subió la tarea %s al curso %s"; +$strings['XUserXUploadedAssignmentXCheckX'] = "%s: El usuario %s subió la tarea %s. Compruébelo en %s"; +$strings['XSubscribedSessionX'] = "%s: Ha sido suscrito a la sesión %s"; +$strings['XSubscribedSessionXCSV'] = "%s: Ha sido suscrito a la sesión %s"; +$strings['XUserXSuggestedBeFriends'] = "%s: El usuario %s le sugirió que podrían ser amigos. Compruebe su bandeja de entrada o su e-mail para más detalles"; +$strings['XUserXAnsweredInboxMessage'] = "%s: El usuario acaba de contestar a su mensaje de la bandeja de entrada"; +$strings['XBeenInvitedJoinGroupX'] = "%s: Ha sido invitado a unirse al grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XMessagesSentEditedGroupXEdited'] = "%s: Hay mensajes enviados o editados en el grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XMessagesSentEditedGroupXAdded'] = "%s: Hay mensajes enviados o editados en el grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XBeenInvitedCompleteSurveyXCourseX'] = "%s: Ha sido invitado a completar la encuesta %s del curso %s"; +$strings['XReminderAssignmentXCourseXDue'] = "%s: Se le recuerda que su tarea (%s) del curso %s aún está pendiente"; +$strings['XUserDetailsModified'] = "%s: Su información de usuario ha sido modificada. Compruebe su e-mail para más información"; diff --git a/plugin/clockworksms/lib/clockworksms.lib.php b/plugin/clockworksms/lib/clockworksms.lib.php new file mode 100644 index 000000000..fb82367ae --- /dev/null +++ b/plugin/clockworksms/lib/clockworksms.lib.php @@ -0,0 +1,676 @@ + + * + * Clockworksms-Chamilo connector class + */ +class Clockworksms implements SmsPluginLibraryInterface +{ + public $apiKey; + public $api; + public $plugin_enabled = false; + + /** + * Constructor (generates a connection to the API). + * + * @param string Clockworksms API key required to use the plugin + */ + public function __construct($apiKey = null) + { + $plugin = ClockworksmsPlugin::create(); + $clockWorkSMSPlugin = $plugin->get('tool_enable'); + if (empty($apiKey)) { + $clockWorkSMSApiKey = $plugin->get('api_key'); + } else { + $clockWorkSMSApiKey = $apiKey; + } + $this->table = Database::get_main_table('user_field_values'); + if ($clockWorkSMSPlugin == true) { + $this->apiKey = $clockWorkSMSApiKey; + // Setting Clockworksms api + if (!defined('CONFIG_SECURITY_API_KEY')) { + define('CONFIG_SECURITY_API_KEY', $this->apiKey); + } + $trimmedApiKey = trim(CONFIG_SECURITY_API_KEY); + if (!empty($trimmedApiKey)) { + $this->api = new Clockwork(CONFIG_SECURITY_API_KEY); + } else { + $this->api = new Clockwork(' '); + $recipient_name = api_get_person_name( + api_get_setting('administratorName'), + api_get_setting('administratorSurname'), + null, + PERSON_NAME_EMAIL_ADDRESS + ); + $email_form = api_get_setting('emailAdministrator'); + $emailsubject = 'Clockworksms error'; + $emailbody = 'Key cannot be blank'; + $sender_name = $recipient_name; + $email_admin = $email_form; + api_mail_html( + $recipient_name, + $email_form, + $emailsubject, + $emailbody, + $sender_name, + $email_admin + ); + } + $this->plugin_enabled = true; + } + } + + /** + * {@inheritdoc} + */ + public function getMobilePhoneNumberById($userId) + { + $mobilePhoneNumberExtraField = new ExtraField('user'); + $mobilePhoneNumberExtraField = $mobilePhoneNumberExtraField->get_handler_field_info_by_field_variable( + 'mobile_phone_number' + ); + + $mobilePhoneNumberExtraFieldValue = new ExtraFieldValue('user'); + $mobilePhoneNumberExtraFieldValue = $mobilePhoneNumberExtraFieldValue->get_values_by_handler_and_field_id( + $userId, + $mobilePhoneNumberExtraField['id'] + ); + + return $mobilePhoneNumberExtraFieldValue['value']; + } + + /** + * send (sends an SMS to the user). + * + * @param array Data needed to send the SMS. It is mandatory to include the + * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least. + * More data may be neccesary depending on the message type + * Example: $additional_parameters = array( + * 'smsType' => EXAMPLE_SMS_TYPE, + * 'userId' => $userId, + * 'moreData' => $moreData + * ); + */ + public function send($additionalParameters) + { + $trimmedKey = trim(CONFIG_SECURITY_API_KEY); + if (!empty($trimmedKey)) { + $phoneExists = array_key_exists('mobilePhoneNumber', $additionalParameters); + $to = $phoneExists ? $additionalParameters['mobilePhoneNumber'] : $this->getMobilePhoneNumberById($additionalParameters['userId']); + + $message = [ + 'to' => $to, + 'message' => $this->getSms($additionalParameters), + ]; + + if (!empty($message['message'])) { + $this->api->send($message); + } + } + } + + /** + * buildSms (builds an SMS from a template and data). + * + * @param ClockworksmsPlugin $plugin + * @param Template $tpl + * @param string Template file name + * @param string $messageKey Text key from lang file + * @param array $parameters Data to fill message variables (if any) + * + * @return string + */ + public function buildSms( + $plugin, + $tpl, + $templateName, + $messageKey, + $parameters = [] + ) { + // Send direct message with out using plugin get_lang + if (isset($parameters['direct_message'])) { + return $parameters['direct_message']; + } + + $message = $plugin->get_lang($messageKey); + if ($parameters !== null) { + $message = vsprintf($message, $parameters); + } + + return $message; + } + + /** + * getSms (returns an SMS message depending of its type). + * + * @param array Data needed to send the SMS. It is mandatory to include the + * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least. + * More data may be neccesary depending on the message type + * Example: $additional_parameters = array( + * 'smsType' => EXAMPLE_SMS_TYPE, + * 'userId' => $userId, + * 'moreData' => $moreData + * ); + * + * @return string A ready to be sent SMS + */ + public function getSms($additionalParameters) + { + $plugin = ClockworksmsPlugin::create(); + $tool_name = $plugin->get_lang('plugin_title'); + $tpl = new Template($tool_name); + + switch ($additionalParameters['smsType']) { + case SmsPlugin::WELCOME_LOGIN_PASSWORD: + $userInfo = api_get_user_info($additionalParameters['userId']); + + return $this->buildSms( + $plugin, + $tpl, + 'welcome_login_password.tpl', + 'WelcomeXLoginXPasswordX', + [ + api_get_setting('siteName'), + $userInfo['username'], + $additionalParameters['password'], + ] + ); + break; + case SmsPlugin::NEW_FILE_SHARED_COURSE_BY: + return $this->buildSms( + $plugin, + $tpl, + 'new_file_shared_course_by.tpl', + 'XNewFileSharedCourseXByX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + $additionalParameters['userUsername'], + ] + ); + break; + case SmsPlugin::ACCOUNT_APPROVED_CONNECT: + return $this->buildSms( + $plugin, + $tpl, + 'account_approved_connect.tpl', + 'XAccountApprovedConnectX', + [ + api_get_setting('siteName'), + $tpl->params['_p']['web'], + ] + ); + break; + case SmsPlugin::NEW_COURSE_BEEN_CREATED: + return $this->buildSms( + $plugin, + $tpl, + 'new_course_been_created.tpl', + 'XNewCourseXBeenCreatedX', + [ + api_get_setting('siteName'), + $additionalParameters['courseName'], + $additionalParameters['creatorUsername'], + ] + ); + break; + case SmsPlugin::NEW_USER_SUBSCRIBED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'new_user_subscribed_course.tpl', + 'XNewUserXSubscribedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['userUsername'], + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::NEW_COURSE_SUGGESTED_TEACHER: + return $this->buildSms( + $plugin, + $tpl, + 'new_course_suggested_teacher.tpl', + 'XNewCourseSuggestedTeacherX', + [ + api_get_setting('siteName'), + $additionalParameters['userUsername'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REGISTERED: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_code_registered.tpl', + 'XCourseOpeningRequestCodeXRegistered', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_APPROVED: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_course_code_approved.tpl', + 'XCourseOpeningRequestCourseCodeXApproved', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REJECTED: + return $this->buildSms( + $plugin, + $tpl, + 'request_open_course_code_rejected.tpl', + 'XRequestOpenCourseCodeXReject', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_course_code.tpl', + 'XCourseOpeningRequestCourseCodeX', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::BEEN_SUBSCRIBED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'been_subscribed_course.tpl', + 'XBeenSubscribedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + ] + ); + break; + case SmsPlugin::ASSIGNMENT_BEEN_CREATED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'assignment_been_created_course.tpl', + 'XAssignmentBeenCreatedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + ] + ); + break; + case SmsPlugin::CERTIFICATE_NOTIFICATION: + return $this->buildSms( + $plugin, + $tpl, + 'certificate_notification.tpl', + '', + $additionalParameters + ); + break; + // Message types to be implemented. Fill the array parameter with arguments. + /*case SmsPlugin::ACCOUNT_CREATED_UPDATED_LOGIN_PASSWORD: + return $this->buildSms( + $plugin, + $tpl, + 'account_created_updated_login_password.tpl', + 'XAccountCreatedUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::PASSWORD_UPDATED_LOGIN_PASSWORD: + return $this->buildSms( + $plugin, + $tpl, + 'password_updated_login_password.tpl', + 'XPasswordUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::REQUESTED_PASSWORD_CHANGE: + return $this->buildSms( + $plugin, + $tpl, + 'requested_password_change.tpl', + 'XPasswordUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::RECEIVED_NEW_PERSONAL_MESSAGES: + return $this->buildSms( + $plugin, + $tpl, + 'received_new_personal_messages.tpl', + 'XReceivedNewPersonalMessages', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_USER_PENDING_APPROVAL: + return $this->buildSms( + $plugin, + $tpl, + 'new_user_pending_approval.tpl', + 'XNewUserXPendingApproval', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::POSTED_FORUM_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'posted_forum_course.tpl', + 'XXPostedForumXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::CHECK_EMAIL_CONNECT_MORE_INFO: + return $this->buildSms( + $plugin, + $tpl, + 'check_email_connect_more_info.tpl', + 'XXXCheckEmailConnectMoreInfo', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test.tpl', + 'XXStudentXAnsweredTestX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST_OPEN_QUESTION: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test_open_question.tpl', + 'XXStudentXAnsweredTestXOpenQuestion', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST_VOICE_QUESTION: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test_voice_question.tpl', + 'XXStudentXAnsweredTestXVoiceQuestion', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::ANSWER_OPEN_QUESTION_TEST_REVIEWED: + return $this->buildSms( + $plugin, + $tpl, + 'answer_open_question_test_reviewed.tpl', + 'XXAnswerOpenQuestionTestXReviewed', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_THREAD_STARTED_FORUM: + return $this->buildSms( + $plugin, + $tpl, + 'new_thread_started_forum.tpl', + 'XXNewThreadXStartedForumX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_ANSWER_POSTED_FORUM: + return $this->buildSms( + $plugin, + $tpl, + 'new_answer_posted_forum.tpl', + 'XXNewAnswerPostedXForumX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_SYSTEM_ANNOUNCEMENT_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'new_system_announcement_added.tpl', + 'XXNewSystemAnnouncementAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::TEST_NEW_SYSTEM_ANNOUNCEMENT_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'test_new_system_announcement_added.tpl', + 'XTestXNewSystemAnnouncementAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SYSTEM_ANNOUNCEMENT_UPDATE: + return $this->buildSms( + $plugin, + $tpl, + 'system_announcement_update.tpl', + 'XXSystemAnnouncementUpdate', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::TEST_SYSTEM_ANNOUNCEMENT_UPDATE: + return $this->buildSms( + $plugin, + $tpl, + 'test_system_announcement_update.tpl', + 'XXSystemAnnouncementUpdate', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE_STUDENT_SUBMITS_PAPER: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_course_student_submits_paper.tpl', + 'XUserXUploadedAssignmentXCourseXStudentSubmitsPaper', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK_STUDENT_SUBMITS_PAPER: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_check_student_submits_paper.tpl', + 'XUserXUploadedAssignmentXCheckXStudentSubmitsPaper', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_course.tpl', + 'XUserXUploadedAssignmentXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_check.tpl', + 'XUserXUploadedAssignmentXCheckX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SUBSCRIBED_SESSION: + return $this->buildSms( + $plugin, + $tpl, + 'subscribed_session.tpl', + 'XSubscribedSessionX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SUBSCRIBED_SESSION_CSV: + return $this->buildSms( + $plugin, + $tpl, + 'subscribed_session_csv.tpl', + 'XSubscribedSessionXCSV', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_SUGGESTED_BE_FRIENDS: + return $this->buildSms( + $plugin, + $tpl, + 'user_suggested_be_friends.tpl', + 'XUserXSuggestedBeFriends', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_ANSWERED_INBOX_MESSAGE: + return $this->buildSms( + $plugin, + $tpl, + 'user_answered_inbox_message.tpl', + 'XUserXAnsweredInboxMessage', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::BEEN_INVITED_JOIN_GROUP: + return $this->buildSms( + $plugin, + $tpl, + 'been_invited_join_group.tpl', + 'XBeenInvitedJoinGroupX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_EDITED: + return $this->buildSms( + $plugin, + $tpl, + 'messages_sent_edited_group_edited.tpl', + 'XMessagesSentEditedGroupXEdited', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'messages_sent_edited_group_added.tpl', + 'XMessagesSentEditedGroupXAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::BEEN_INVITED_COMPLETE_SURVEY_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'been_invited_complete_survey_course.tpl', + 'XBeenInvitedCompleteSurveyXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::REMINDER_ASSIGNMENT_COURSE_DUE: + return $this->buildSms( + $plugin, + $tpl, + 'reminder_assignment_course_due.tpl', + 'XReminderAssignmentXCourseXDue', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_DETAILS_MODIFIED: + return $this->buildSms( + $plugin, + $tpl, + 'user_details_modified.tpl', + 'XUserDetailsModified', + array( + api_get_setting('siteName') + ) + ); + break;*/ + default: + return ''; + } + } +} diff --git a/plugin/clockworksms/lib/clockworksms_plugin.class.php b/plugin/clockworksms/lib/clockworksms_plugin.class.php new file mode 100644 index 000000000..257860ae9 --- /dev/null +++ b/plugin/clockworksms/lib/clockworksms_plugin.class.php @@ -0,0 +1,50 @@ + + * @author Julio Montoya - Refactor code + */ +class ClockworksmsPlugin extends SmsPlugin +{ + /** + * Constructor. + */ + public function __construct() + { + $fields = ['tool_enable' => 'boolean', 'api_key' => 'text']; + $smsTypeOptions = $this->getSmsTypeOptions(); + foreach ($smsTypeOptions as $smsTypeOption) { + $fields[$smsTypeOption] = 'checkbox'; + } + parent::__construct('0.1', 'Imanol Losada', $fields); + } + + /** + * create (a singleton function that ensures ClockworksmsPlugin instance is + * created only once. If it is already created, it returns the instance). + * + * @return object ClockworksmsPlugin instance + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * install (uninstalls the plugin and removes all plugin's tables and/or rows). + */ + public function uninstall() + { + $tSettings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); + $sql = "DELETE FROM $tSettings WHERE subkey = 'clockworksms'"; + Database::query($sql); + } +} diff --git a/plugin/clockworksms/license.txt b/plugin/clockworksms/license.txt new file mode 100644 index 000000000..b70d65ea2 --- /dev/null +++ b/plugin/clockworksms/license.txt @@ -0,0 +1,5 @@ +This plugin, as the rest of Chamilo, is released under the GNU/GPLv3 license. + +The Clockworks library included is provided under the ICS license, compatible +with GNU/GPLv3. See https://github.com/mediaburst/clockwork-php/issues/3 for +details. \ No newline at end of file diff --git a/plugin/clockworksms/plugin.php b/plugin/clockworksms/plugin.php new file mode 100644 index 000000000..bdc6f9fa4 --- /dev/null +++ b/plugin/clockworksms/plugin.php @@ -0,0 +1,11 @@ + + */ +require_once __DIR__.'/config.php'; + +$plugin_info = ClockworksmsPlugin::create()->get_info(); diff --git a/plugin/clockworksms/sms_templates/account_approved_connect.tpl b/plugin/clockworksms/sms_templates/account_approved_connect.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/account_approved_connect.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/account_created_updated_login_password.tpl b/plugin/clockworksms/sms_templates/account_created_updated_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/account_created_updated_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/answer_open_question_test_reviewed.tpl b/plugin/clockworksms/sms_templates/answer_open_question_test_reviewed.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/answer_open_question_test_reviewed.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/assignment_been_created_course.tpl b/plugin/clockworksms/sms_templates/assignment_been_created_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/assignment_been_created_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/been_invited_complete_survey_course.tpl b/plugin/clockworksms/sms_templates/been_invited_complete_survey_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/been_invited_complete_survey_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/been_invited_join_group.tpl b/plugin/clockworksms/sms_templates/been_invited_join_group.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/been_invited_join_group.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/been_subscribed_course.tpl b/plugin/clockworksms/sms_templates/been_subscribed_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/been_subscribed_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/certificate_notification.tpl b/plugin/clockworksms/sms_templates/certificate_notification.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/certificate_notification.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/check_email_connect_more_info.tpl b/plugin/clockworksms/sms_templates/check_email_connect_more_info.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/check_email_connect_more_info.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/course_opening_request_code_registered.tpl b/plugin/clockworksms/sms_templates/course_opening_request_code_registered.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/course_opening_request_code_registered.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/course_opening_request_course_code.tpl b/plugin/clockworksms/sms_templates/course_opening_request_course_code.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/course_opening_request_course_code.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/course_opening_request_course_code_approved.tpl b/plugin/clockworksms/sms_templates/course_opening_request_course_code_approved.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/course_opening_request_course_code_approved.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/messages_sent_edited_group_added.tpl b/plugin/clockworksms/sms_templates/messages_sent_edited_group_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/messages_sent_edited_group_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/messages_sent_edited_group_edited.tpl b/plugin/clockworksms/sms_templates/messages_sent_edited_group_edited.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/messages_sent_edited_group_edited.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_answer_posted_forum.tpl b/plugin/clockworksms/sms_templates/new_answer_posted_forum.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_answer_posted_forum.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_course_been_created.tpl b/plugin/clockworksms/sms_templates/new_course_been_created.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_course_been_created.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_course_suggested_teacher.tpl b/plugin/clockworksms/sms_templates/new_course_suggested_teacher.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_course_suggested_teacher.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_file_shared_course_by.tpl b/plugin/clockworksms/sms_templates/new_file_shared_course_by.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_file_shared_course_by.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_system_announcement_added.tpl b/plugin/clockworksms/sms_templates/new_system_announcement_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_system_announcement_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_thread_started_forum.tpl b/plugin/clockworksms/sms_templates/new_thread_started_forum.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_thread_started_forum.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_user_pending_approval.tpl b/plugin/clockworksms/sms_templates/new_user_pending_approval.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_user_pending_approval.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/new_user_subscribed_course.tpl b/plugin/clockworksms/sms_templates/new_user_subscribed_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/new_user_subscribed_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/password_updated_login_password.tpl b/plugin/clockworksms/sms_templates/password_updated_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/password_updated_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/posted_forum_course.tpl b/plugin/clockworksms/sms_templates/posted_forum_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/posted_forum_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/received_new_personal_messages.tpl b/plugin/clockworksms/sms_templates/received_new_personal_messages.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/received_new_personal_messages.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/reminder_assignment_course_due.tpl b/plugin/clockworksms/sms_templates/reminder_assignment_course_due.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/reminder_assignment_course_due.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/request_open_course_code_rejected.tpl b/plugin/clockworksms/sms_templates/request_open_course_code_rejected.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/request_open_course_code_rejected.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/requested_password_change.tpl b/plugin/clockworksms/sms_templates/requested_password_change.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/requested_password_change.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/student_answered_test.tpl b/plugin/clockworksms/sms_templates/student_answered_test.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/student_answered_test.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/student_answered_test_open_question.tpl b/plugin/clockworksms/sms_templates/student_answered_test_open_question.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/student_answered_test_open_question.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/student_answered_test_voice_question.tpl b/plugin/clockworksms/sms_templates/student_answered_test_voice_question.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/student_answered_test_voice_question.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/subscribed_session.tpl b/plugin/clockworksms/sms_templates/subscribed_session.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/subscribed_session.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/subscribed_session_csv.tpl b/plugin/clockworksms/sms_templates/subscribed_session_csv.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/subscribed_session_csv.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/system_announcement_update.tpl b/plugin/clockworksms/sms_templates/system_announcement_update.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/system_announcement_update.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/test_new_system_announcement_added.tpl b/plugin/clockworksms/sms_templates/test_new_system_announcement_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/test_new_system_announcement_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/test_system_announcement_update.tpl b/plugin/clockworksms/sms_templates/test_system_announcement_update.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/test_system_announcement_update.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_answered_inbox_message.tpl b/plugin/clockworksms/sms_templates/user_answered_inbox_message.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_answered_inbox_message.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_details_modified.tpl b/plugin/clockworksms/sms_templates/user_details_modified.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_details_modified.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_suggested_be_friends.tpl b/plugin/clockworksms/sms_templates/user_suggested_be_friends.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_suggested_be_friends.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_uploaded_assignment_check.tpl b/plugin/clockworksms/sms_templates/user_uploaded_assignment_check.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_uploaded_assignment_check.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl b/plugin/clockworksms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_uploaded_assignment_course.tpl b/plugin/clockworksms/sms_templates/user_uploaded_assignment_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_uploaded_assignment_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl b/plugin/clockworksms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/sms_templates/welcome_login_password.tpl b/plugin/clockworksms/sms_templates/welcome_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/clockworksms/sms_templates/welcome_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/clockworksms/uninstall.php b/plugin/clockworksms/uninstall.php new file mode 100644 index 000000000..34e9e6f8d --- /dev/null +++ b/plugin/clockworksms/uninstall.php @@ -0,0 +1,14 @@ + + */ +require_once __DIR__.'/config.php'; +ClockworksmsPlugin::create()->uninstall(); diff --git a/plugin/clockworksms/vendor/changelog.md b/plugin/clockworksms/vendor/changelog.md new file mode 100644 index 000000000..bf6023876 --- /dev/null +++ b/plugin/clockworksms/vendor/changelog.md @@ -0,0 +1,22 @@ +# Changelog + +## 1.0 (July 19th, 2012) + +* Initial release. [JI/MS] + +### 1.1 (August 21st, 2012) + +* Added /get_key functionality to translate a legacy Mediaburst username and password into a new Clockwork API key. [JI] +* Deprecated `checkCredit()` and replaced with `checkBalance()` [JI] + +### 1.2 (September 7th, 2012) + +* Added various new Wordpress classes, including the Clockwork_Plugin class for writing plugins based on Clockwork. [JI] + +### 1.3 (September 18th, 2012) + +* Added `is_valid_msisdn()` method. [JI] + +#### 1.3.1 (November 13th, 2012) + +* Updated `is_valid_msisdn()` method to handle 9-digit phone numbers, e.g. Norway. [JI] \ No newline at end of file diff --git a/plugin/clockworksms/vendor/clockworksms_api.php b/plugin/clockworksms/vendor/clockworksms_api.php new file mode 100644 index 000000000..2c3662421 --- /dev/null +++ b/plugin/clockworksms/vendor/clockworksms_api.php @@ -0,0 +1,643 @@ +key = $key; + } + + $this->ssl = (array_key_exists('ssl', $options)) ? $options['ssl'] : null; + $this->proxy_host = (array_key_exists('proxy_host', $options)) ? $options['proxy_host'] : null; + $this->proxy_port = (array_key_exists('proxy_port', $options)) ? $options['proxy_port'] : null; + $this->from = (array_key_exists('from', $options)) ? $options['from'] : null; + $this->long = (array_key_exists('long', $options)) ? $options['long'] : null; + $this->truncate = (array_key_exists('truncate', $options)) ? $options['truncate'] : null; + $this->invalid_char_action = (array_key_exists('invalid_char_action', $options)) ? $options['invalid_char_action'] : null; + $this->log = (array_key_exists('log', $options)) ? $options['log'] : false; + } + + /** + * Send some text messages + * + * + * @author Martin Steel + */ + public function send(array $sms) { + if (!is_array($sms)) { + throw new ClockworkException("sms parameter must be an array"); + } + $single_message = $this->is_assoc($sms); + + if ($single_message) { + $sms = array($sms); + } + + $req_doc = new DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Message'); + $req_doc->appendChild($root); + + $user_node = $req_doc->createElement('Key'); + $user_node->appendChild($req_doc->createTextNode($this->key)); + $root->appendChild($user_node); + + for ($i = 0; $i < count($sms); $i++) { + $single = $sms[$i]; + + $sms_node = $req_doc->createElement('SMS'); + + // Phone number + $sms_node->appendChild($req_doc->createElement('To', $single['to'])); + + // Message text + $content_node = $req_doc->createElement('Content'); + $content_node->appendChild($req_doc->createTextNode($single['message'])); + $sms_node->appendChild($content_node); + + // From + if (array_key_exists('from', $single) || isset($this->from)) { + $from_node = $req_doc->createElement('From'); + $from_node->appendChild($req_doc->createTextNode(array_key_exists('from', $single) ? $single['from'] : $this->from)); + $sms_node->appendChild($from_node); + } + + // Client ID + if (array_key_exists('client_id', $single)) { + $client_id_node = $req_doc->createElement('ClientID'); + $client_id_node->appendChild($req_doc->createTextNode($single['client_id'])); + $sms_node->appendChild($client_id_node); + } + + // Long + if (array_key_exists('long', $single) || isset($this->long)) { + $long = array_key_exists('long', $single) ? $single['long'] : $this->long; + $long_node = $req_doc->createElement('Long'); + $long_node->appendChild($req_doc->createTextNode($long ? 1 : 0)); + $sms_node->appendChild($long_node); + } + + // Truncate + if (array_key_exists('truncate', $single) || isset($this->truncate)) { + $truncate = array_key_exists('truncate', $single) ? $single['truncate'] : $this->truncate; + $trunc_node = $req_doc->createElement('Truncate'); + $trunc_node->appendChild($req_doc->createTextNode($truncate ? 1 : 0)); + $sms_node->appendChild($trunc_node); + } + + // Invalid Char Action + if (array_key_exists('invalid_char_action', $single) || isset($this->invalid_char_action)) { + $action = array_key_exists('invalid_char_action', $single) ? $single['invalid_char_action'] : $this->invalid_char_action; + switch (strtolower($action)) { + case 'error': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 1)); + break; + case 'remove': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 2)); + break; + case 'replace': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 3)); + break; + default: + break; + } + } + + // Wrapper ID + $sms_node->appendChild($req_doc->createElement('WrapperID', $i)); + + $root->appendChild($sms_node); + } + + $req_xml = $req_doc->saveXML(); + + $resp_xml = $this->postToClockwork(self::API_SMS_METHOD, $req_xml); + $resp_doc = new DOMDocument(); + $resp_doc->loadXML($resp_xml); + + $response = array(); + $err_no = null; + $err_desc = null; + + foreach($resp_doc->documentElement->childNodes AS $doc_child) { + switch(strtolower($doc_child->nodeName)) { + case 'sms_resp': + $resp = array(); + $wrapper_id = null; + foreach($doc_child->childNodes AS $resp_node) { + switch(strtolower($resp_node->nodeName)) { + case 'messageid': + $resp['id'] = $resp_node->nodeValue; + break; + case 'errno': + $resp['error_code'] = $resp_node->nodeValue; + break; + case 'errdesc': + $resp['error_message'] = $resp_node->nodeValue; + break; + case 'wrapperid': + $wrapper_id = $resp_node->nodeValue; + break; + } + } + if( array_key_exists('error_code', $resp ) ) + { + $resp['success'] = 0; + } else { + $resp['success'] = 1; + } + $resp['sms'] = $sms[$wrapper_id]; + array_push($response, $resp); + break; + case 'errno': + $err_no = $doc_child->nodeValue; + break; + case 'errdesc': + $err_desc = $doc_child->nodeValue; + break; + } + } + + if (isset($err_no)) { + throw new ClockworkException($err_desc, $err_no); + } + + if ($single_message) { + return $response[0]; + } else { + return $response; + } + } + + /** + * Check how many SMS credits you have available + * + * @return integer SMS credits remaining + * @deprecated Use checkBalance() instead + * @author Martin Steel + */ + public function checkCredit() { + // Create XML doc for request + $req_doc = new DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Credit'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Clockwork + $resp_xml = $this->postToClockwork(self::API_CREDIT_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to find credit value + $credit; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes AS $doc_child) { + switch ($doc_child->nodeName) { + case "Credit": + $credit = $doc_child->nodeValue; + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new ClockworkException($err_desc, $err_no); + } + return $credit; + } + + /** + * Check your account balance + * + * @return array Array of account balance: + * @author Martin Steel + */ + public function checkBalance() { + // Create XML doc for request + $req_doc = new DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Balance'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Clockwork + $resp_xml = $this->postToClockwork(self::API_BALANCE_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to find balance value + $balance = null; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes as $doc_child) { + switch ($doc_child->nodeName) { + case "Balance": + $balance = number_format(floatval($doc_child->nodeValue), 2); + break; + case "Currency": + foreach ($doc_child->childNodes as $resp_node) { + switch ($resp_node->tagName) { + case "Symbol": + $symbol = $resp_node->nodeValue; + break; + case "Code": + $code = $resp_node->nodeValue; + break; + } + } + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new ClockworkException($err_desc, $err_no); + } + + return array( 'symbol' => $symbol, 'balance' => $balance, 'code' => $code ); + } + + /** + * Check whether the API Key is valid + * + * @return bool True indicates a valid key + * @author Martin Steel + */ + public function checkKey() { + // Create XML doc for request + $req_doc = new DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Authenticate'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Clockwork + $resp_xml = $this->postToClockwork(self::API_AUTH_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to see if authenticated + $cust_id; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes AS $doc_child) { + switch ($doc_child->nodeName) { + case "CustID": + $cust_id = $doc_child->nodeValue; + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new ClockworkException($err_desc, $err_no); + } + return isset($cust_id); + } + + /** + * Make an HTTP POST to Clockwork + * + * @param string method Clockwork method to call (sms/credit) + * @param string data Content of HTTP POST + * + * @return string Response from Clockwork + * @author Martin Steel + */ + protected function postToClockwork($method, $data) { + if ($this->log) { + $this->logXML("API $method Request XML", $data); + } + + if( isset( $this->ssl ) ) { + $ssl = $this->ssl; + } else { + $ssl = $this->sslSupport(); + } + + $url = $ssl ? 'https://' : 'http://'; + $url .= self::API_BASE_URL . $method; + + $response = $this->xmlPost($url, $data); + + if ($this->log) { + $this->logXML("API $method Response XML", $response); + } + + return $response; + } + + /** + * Make a HTTP POST + * + * cURL will be used if available, otherwise tries the PHP stream functions + * + * @param string url URL to send to + * @param string data Data to POST + * @return string Response returned by server + * @author Martin Steel + */ + protected function xmlPost($url, $data) { + if(extension_loaded('curl')) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml")); + curl_setopt($ch, CURLOPT_USERAGENT, 'Clockwork PHP Wrapper/1.0' . self::VERSION); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + if (isset($this->proxy_host) && isset($this->proxy_port)) { + curl_setopt($ch, CURLOPT_PROXY, $this->proxy_host); + curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port); + } + + $response = curl_exec($ch); + $info = curl_getinfo($ch); + + if ($response === false || $info['http_code'] != 200) { + throw new Exception('HTTP Error calling Clockwork API - HTTP Status: ' . $info['http_code'] . ' - cURL Erorr: ' . curl_error($ch)); + } elseif (curl_errno($ch) > 0) { + throw new Exception('HTTP Error calling Clockwork API - cURL Error: ' . curl_error($ch)); + } + + curl_close($ch); + + return $response; + } elseif (function_exists('stream_get_contents')) { + // Enable error Track Errors + $track = ini_get('track_errors'); + ini_set('track_errors',true); + + $params = array('http' => array( + 'method' => 'POST', + 'header' => "Content-Type: text/xml\r\nUser-Agent: mediaburst PHP Wrapper/" . self::VERSION . "\r\n", + 'content' => $data + )); + + if (isset($this->proxy_host) && isset($this->proxy_port)) { + $params['http']['proxy'] = 'tcp://'.$this->proxy_host . ':' . $this->proxy_port; + $params['http']['request_fulluri'] = True; + } + + $ctx = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $ctx); + if (!$fp) { + ini_set('track_errors',$track); + throw new Exception("HTTP Error calling Clockwork API - fopen Error: $php_errormsg"); + } + $response = @stream_get_contents($fp); + if ($response === false) { + ini_set('track_errors',$track); + throw new Exception("HTTP Error calling Clockwork API - stream Error: $php_errormsg"); + } + ini_set('track_errors',$track); + return $response; + } else { + throw new Exception("Clockwork requires PHP5 with cURL or HTTP stream support"); + } + } + + /** + * Does the server/HTTP wrapper support SSL + * + * This is a best guess effort, some servers have weird setups where even + * though cURL is compiled with SSL support is still fails to make + * any requests. + * + * @return bool True if SSL is supported + * @author Martin Steel + */ + protected function sslSupport() { + $ssl = false; + // See if PHP is compiled with cURL + if (extension_loaded('curl')) { + $version = curl_version(); + $ssl = ($version['features'] & CURL_VERSION_SSL) ? true : false; + } elseif (extension_loaded('openssl')) { + $ssl = true; + } + return $ssl; + } + + /** + * Log some XML, tidily if possible, in the PHP error log + * + * @param string log_msg The log message to prepend to the XML + * @param string xml An XML formatted string + * + * @return void + * @author Martin Steel + */ + protected function logXML($log_msg, $xml) { + // Tidy if possible + if (class_exists('tidy')) { + $tidy = new tidy; + $config = array( + 'indent' => true, + 'input-xml' => true, + 'output-xml' => true, + 'wrap' => 200 + ); + $tidy->parseString($xml, $config, 'utf8'); + $tidy->cleanRepair(); + $xml = $tidy; + } + // Output + error_log("Clockwork $log_msg: $xml"); + } + + /** + * Check if an array is associative + * + * @param array $array Array to check + * @return bool + * @author Martin Steel + */ + protected function is_assoc($array) { + return (bool)count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Check if a number is a valid MSISDN + * + * @param string $val Value to check + * @return bool True if valid MSISDN + * @author James Inman + * @since 1.3.0 + * @todo Take an optional country code and check that the number starts with it + */ + public static function is_valid_msisdn($val) { + return preg_match( '/^[1-9][0-9]{7,12}$/', $val ); + } + +} diff --git a/plugin/clockworksms/vendor/exception.php b/plugin/clockworksms/vendor/exception.php new file mode 100644 index 000000000..bf8d63ee8 --- /dev/null +++ b/plugin/clockworksms/vendor/exception.php @@ -0,0 +1,27 @@ + + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/plugin/clockworksms/vendor/license.txt~ b/plugin/clockworksms/vendor/license.txt~ new file mode 100644 index 000000000..382c1173e --- /dev/null +++ b/plugin/clockworksms/vendor/license.txt~ @@ -0,0 +1,13 @@ +Copyright (c) 2011 - 2012, Mediaburst Ltd + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/plugin/clockworksms/vendor/readme.md b/plugin/clockworksms/vendor/readme.md new file mode 100644 index 000000000..fc536fb62 --- /dev/null +++ b/plugin/clockworksms/vendor/readme.md @@ -0,0 +1,253 @@ +# Clockwork SMS API Wrapper for PHP + +This wrapper lets you interact with Clockwork without the hassle of having to create any XML or make HTTP calls. + +## What's Clockwork? + +[Clockwork][2] is Mediaburst's SMS API. + +### Prerequisites + +* A [Clockwork][2] account + +## Usage + +Require the Clockwork library: + +```php +require 'class-Clockwork.php'; +``` + +### Sending a message + +```php +$clockwork = new Clockwork( $API_KEY ); +$message = array( 'to' => '441234567891', 'message' => 'This is a test!' ); +$result = $clockwork->send( $message ); +``` + +### Sending multiple messages + +We recommend you use batch sizes of 500 messages or fewer. By limiting the batch size it prevents any timeouts when sending. + +```php +$clockwork = new Clockwork( $API_KEY ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!' ) +); +$results = $clockwork->send( $messages ); +``` + +### Handling the response + +The responses come back as arrays, these contain the unique Clockwork message ID, whether the message worked (`success`), and the original SMS so you can update your database. + + Array + ( + [id] => VE_164732148 + [success] => 1 + [sms] => Array + ( + [to] => 441234567891 + [message] => This is a test! + ) + + ) + +If you send multiple SMS messages in a single send, you'll get back an array of results, one per SMS. + +The result will look something like this: + + Array + ( + [0] => Array + ( + [id] => VI_143228951 + [success] => 1 + [sms] => Array + ( + [to] => 441234567891 + [message] => This is a test! + ) + + ) + + [1] => Array + ( + [id] => VI_143228952 + [success] => 1 + [sms] => Array + ( + [to] => 441234567892 + [message] => This is a test 2! + ) + + ) + + ) + +If a message fails, the reason for failure will be set in `error_code` and `error_message`. + +For example, if you send to invalid phone number "abc": + + Array + ( + [error_code] => 10 + [error_message] => Invalid 'To' Parameter + [success] => 0 + [sms] => Array + ( + [to] => abc + [message] => This is a test! + ) + + ) + +### Checking your balance + +Check your available SMS balance: + +```php +$clockwork = new Clockwork( $API_KEY ); +$clockwork->checkBalance(); +``` + +This will return: + + Array + ( + [symbol] => £ + [balance] => 351.91 + [code] => GBP + ) + +### Handling Errors + +The Clockwork wrapper will throw a `ClockworkException` if the entire call failed. + +```php +try +{ + $clockwork = new Clockwork( 'invalid_key' ); + $message = array( 'to' => 'abc', 'message' => 'This is a test!' ); + $result = $clockwork->send( $message ); +} +catch( ClockworkException $e ) +{ + print $e->getMessage(); + // Invalid API Key +} +``` + +### Advanced Usage + +This class has a few additional features that some users may find useful, if these are not set your account defaults will be used. + +### Optional Parameters + +See the [Clockwork Documentation](http://www.clockworksms.com/doc/clever-stuff/xml-interface/send-sms/) for full details on these options. + +* $from [string] + + The from address displayed on a phone when they receive a message + +* $long [boolean] + + Enable long SMS. A standard text can contain 160 characters, a long SMS supports up to 459. + +* $truncate [nullable boolean] + + Truncate the message payload if it is too long, if this is set to false, the message will fail if it is too long. + +* $invalid_char_action [string] + + What to do if the message contains an invalid character. Possible values are + * error - Fail the message + * remove - Remove the invalid characters then send + * replace - Replace some common invalid characters such as replacing curved quotes with straight quotes + +* $ssl [boolean, default: true] + + Use SSL when making an HTTP request to the Clockwork API + + +### Setting Options + +#### Global Options + +Options set on the API object will apply to all SMS messages unless specifically overridden. + +In this example both messages will be sent from Clockwork: + +```php +$options = array( 'from' => 'Clockwork' ); +$clockwork = new Clockwork( $API_KEY, $options ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!' ) +); +$results = $clockwork->send( $messages ); +``` + +#### Per-message Options + +Set option values individually on each message. + +In this example, one message will be from Clockwork and the other from 84433: + +```php +$clockwork = new Clockwork( $API_KEY, $options ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!', 'from' => 'Clockwork' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!', 'from' => '84433' ) +); +$results = $clockwork->send( $messages ); +``` + +### SSL Errors + +Due to the huge variety of PHP setups out there a small proportion of users may get PHP errors when making API calls due to their SSL configuration. + +The errors will generally look something like this: + +``` +Fatal error: +Uncaught exception 'Exception' with message 'HTTP Error calling Clockwork API +HTTP Status: 0 +cURL Erorr: SSL certificate problem, verify that the CA cert is OK. +Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed' +``` + +If you're seeing this error there are two fixes available, the first is easy, simply disable SSL on Clockwork calls. Alternatively you can setup your PHP install with the correct root certificates. + +#### Disable SSL on Clockwork calls + +```php +$options = array( 'ssl' => false ); +$clockwork = new Clockwork( $API_KEY, $options ); +``` + +#### Setup SSL root certificates on your server + +This is much more complicated as it depends on your setup, however there are many guides available online. +Try a search term like "windows php curl root certificates" or "ubuntu update root certificates". + + +# License + +This project is licensed under the ISC open-source license. + +A copy of this license can be found in license.txt. + +# Contributing + +If you have any feedback on this wrapper drop us an email to [hello@clockworksms.com][1]. + +The project is hosted on GitHub at [https://github.com/mediaburst/clockwork-php][3]. +If you would like to contribute a bug fix or improvement please fork the project +and submit a pull request. + +[1]: mailto:hello@clockworksms.com +[2]: http://www.clockworksms.com/ +[3]: https://github.com/mediaburst/clockwork-php diff --git a/plugin/courseblock/CourseBlockPlugin.php b/plugin/courseblock/CourseBlockPlugin.php new file mode 100644 index 000000000..e38c94cde --- /dev/null +++ b/plugin/courseblock/CourseBlockPlugin.php @@ -0,0 +1,94 @@ + 'course_block_pre_footer', + 'type' => 'textarea', + ], + [ + 'name' => 'course_block_footer_left', + 'type' => 'textarea', + ], + [ + 'name' => 'course_block_footer_center', + 'type' => 'textarea', + ], + [ + 'name' => 'course_block_footer_right', + 'type' => 'textarea', + ], + ]; + + protected function __construct() + { + parent::__construct( + '0.1', + 'Julio Montoya', + [ + 'tool_enable' => 'boolean', + ] + ); + } + + /** + * @return CourseBlockPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + public function install() + { + // Installing course settings + $this->install_course_fields_in_all_courses(false); + } + + public function uninstall() + { + // Deleting course settings + $this->uninstall_course_fields_in_all_courses(); + } + + /** + * @param string $region + * + * @return string + */ + public function renderRegion($region) + { + $content = ''; + switch ($region) { + case 'footer_left': + $content = api_get_course_setting('course_block_footer_left'); + $content = $content === -1 ? '' : $content; + break; + case 'footer_center': + $content = api_get_course_setting('course_block_footer_center'); + $content = $content === -1 ? '' : $content; + break; + case 'footer_right': + $content = api_get_course_setting('course_block_footer_right'); + $content = $content === -1 ? '' : $content; + break; + case 'pre_footer': + $content = api_get_course_setting('course_block_pre_footer'); + $content = $content === -1 ? '' : $content; + break; + } + + return $content; + } +} diff --git a/plugin/courseblock/README.md b/plugin/courseblock/README.md new file mode 100644 index 000000000..fd5809f14 --- /dev/null +++ b/plugin/courseblock/README.md @@ -0,0 +1,8 @@ +1. Enabled the plugin from the list of plugins. +2. Click "Configure" once the plugin was enabled. +3. Select tool_enable = Yes and save. +4. Go into a *course* (not course session) and enter the Settings tool. +5. In the "Course block" section fill the footer options. +6. Once the form was saved, you will notice that the text you fill in step .5 + will appear in the Chamilo footer. + diff --git a/plugin/courseblock/config.php b/plugin/courseblock/config.php new file mode 100644 index 000000000..46e612525 --- /dev/null +++ b/plugin/courseblock/config.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/courseblock/lang/english.php b/plugin/courseblock/lang/english.php new file mode 100644 index 000000000..a63dec4dd --- /dev/null +++ b/plugin/courseblock/lang/english.php @@ -0,0 +1,11 @@ +get_info(); diff --git a/plugin/coursehomenotify/CourseHomeNotifyPlugin.php b/plugin/coursehomenotify/CourseHomeNotifyPlugin.php new file mode 100644 index 000000000..2eedb8f16 --- /dev/null +++ b/plugin/coursehomenotify/CourseHomeNotifyPlugin.php @@ -0,0 +1,202 @@ + 'boolean', + ]; + + parent::__construct('0.1', 'Angel Fernando Quiroz Campos', $settings); + + $this->isCoursePlugin = true; + $this->addCourseTool = false; + $this->setCourseSettings(); + } + + /** + * @return CourseHomeNotifyPlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Install process. + * Create table in database. And setup Doctirne entity. + * + * @throws \Doctrine\ORM\Tools\ToolsException + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist(['course_home_notify_notification'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Notification::class), + $em->getClassMetadata(NotificationRelUser::class), + ] + ); + } + + /** + * Uninstall process. + * Remove Doctrine entity. And drop table in database. + */ + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist(['course_home_notify_notification'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Notification::class), + $em->getClassMetadata(NotificationRelUser::class), + ] + ); + } + + /** + * @param string $region + * + * @return string + */ + public function renderRegion($region) + { + if ( + 'main_bottom' !== $region + || strpos($_SERVER['SCRIPT_NAME'], 'course_home/course_home.php') === false + ) { + return ''; + } + + $courseId = api_get_course_int_id(); + $userId = api_get_user_id(); + + if (empty($courseId) || empty($userId)) { + return ''; + } + + $course = api_get_course_entity($courseId); + $user = api_get_user_entity($userId); + + $em = Database::getManager(); + /** @var Notification $notification */ + $notification = $em + ->getRepository('ChamiloPluginBundle:CourseHomeNotify\Notification') + ->findOneBy(['course' => $course]); + + if (!$notification) { + return ''; + } + + $modalFooter = ''; + $modalConfig = ['show' => true]; + + if ($notification->getExpirationLink()) { + /** @var NotificationRelUser $notificationUser */ + $notificationUser = $em + ->getRepository('ChamiloPluginBundle:CourseHomeNotify\NotificationRelUser') + ->findOneBy(['notification' => $notification, 'user' => $user]); + + if ($notificationUser) { + return ''; + } + + $contentUrl = api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/content.php?hash='.$notification->getHash(); + $link = Display::toolbarButton( + $this->get_lang('PleaseFollowThisLink'), + $contentUrl, + 'external-link', + 'link', + ['id' => 'course-home-notify-link', 'target' => '_blank'] + ); + + $modalConfig['keyboard'] = false; + $modalConfig['backdrop'] = 'static'; + + $modalFooter = ''; + } + + $modal = ''; + + $modal .= ""; + + return $modal; + } + + /** + * Set the course settings. + */ + private function setCourseSettings() + { + if ('true' !== $this->get(self::SETTING_ENABLED)) { + return; + } + + $name = $this->get_name(); + + $button = Display::toolbarButton( + $this->get_lang('SetNotification'), + api_get_path(WEB_PLUGIN_PATH).$name.'/configure.php?'.api_get_cidreq(), + 'cog', + 'primary' + ); + + $this->course_settings = [ + [ + 'name' => '

    '.$this->get_comment().'

    '.$button.'
    ', + 'type' => 'html', + ], + ]; + } +} diff --git a/plugin/coursehomenotify/Entity/Notification.php b/plugin/coursehomenotify/Entity/Notification.php new file mode 100644 index 000000000..7d14e9698 --- /dev/null +++ b/plugin/coursehomenotify/Entity/Notification.php @@ -0,0 +1,152 @@ +id; + } + + /** + * @param int $id + * + * @return Notification + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @param string $content + * + * @return Notification + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * @return string + */ + public function getExpirationLink() + { + return $this->expirationLink; + } + + /** + * @param string $expirationLink + * + * @return Notification + */ + public function setExpirationLink($expirationLink) + { + $this->expirationLink = $expirationLink; + + return $this; + } + + /** + * @return string + */ + public function getHash() + { + return $this->hash; + } + + /** + * @param string $hash + * + * @return Notification + */ + public function setHash($hash) + { + $this->hash = $hash; + + return $this; + } + + /** + * @return Course + */ + public function getCourse() + { + return $this->course; + } + + /** + * @param Course $course + * + * @return Notification + */ + public function setCourse($course) + { + $this->course = $course; + + return $this; + } +} diff --git a/plugin/coursehomenotify/Entity/NotificationRelUser.php b/plugin/coursehomenotify/Entity/NotificationRelUser.php new file mode 100644 index 000000000..046747376 --- /dev/null +++ b/plugin/coursehomenotify/Entity/NotificationRelUser.php @@ -0,0 +1,99 @@ +id; + } + + /** + * @param int $id + * + * @return NotificationRelUser + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return Notification + */ + public function getNotification() + { + return $this->notification; + } + + /** + * @return NotificationRelUser + */ + public function setNotification(Notification $notification) + { + $this->notification = $notification; + + return $this; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @param User $user + * + * @return NotificationRelUser + */ + public function setUser($user) + { + $this->user = $user; + + return $this; + } +} diff --git a/plugin/coursehomenotify/README.md b/plugin/coursehomenotify/README.md new file mode 100644 index 000000000..a9827136b --- /dev/null +++ b/plugin/coursehomenotify/README.md @@ -0,0 +1,17 @@ +# Notify in course home + +Show notifications when a user enter in course home page. + +## Set up + +*Prior to installing/uninstalling this plugin, you will need to make sure the src/Chamilo/PluginBundle/Entity folder is +temporarily writeable by the web server.* + +* Install the plugin and enable. +* Go to Settings tool in course base. +* Display the _Notify in course home_ section. And set the notification. + +## Adding a notification +The notification has a HTML content and an optional _Expiration link_ field. +If this field is set, then the notification will be displayed until the user visualizes it. +Otherwise the notification will be displayed every time the user enter to the course home page. diff --git a/plugin/coursehomenotify/configure.php b/plugin/coursehomenotify/configure.php new file mode 100644 index 000000000..58644890d --- /dev/null +++ b/plugin/coursehomenotify/configure.php @@ -0,0 +1,100 @@ +get(CourseHomeNotifyPlugin::SETTING_ENABLED) +) { + api_not_allowed(true); +} + +$action = isset($_GET['action']) ? $_GET['action'] : ''; + +$course = api_get_course_entity($courseId); + +$em = Database::getManager(); +/** @var Notification $notification */ +$notification = $em + ->getRepository('ChamiloPluginBundle:CourseHomeNotify\Notification') + ->findOneBy(['course' => $course]); + +$actionLinks = ''; + +if ($notification) { + $actionLinks = Display::url( + Display::return_icon('delete.png', $plugin->get_lang('DeleteNotification'), [], ICON_SIZE_MEDIUM), + api_get_self().'?'.api_get_cidreq().'&action=delete' + ); + + if ('delete' === $action) { + $em->remove($notification); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('NotificationDeleted'), 'success') + ); + + header('Location: '.api_get_course_url()); + exit; + } +} else { + $notification = new Notification(); +} + +$form = new FormValidator('frm_course_home_notify'); +$form->addHeader($plugin->get_lang('AddNotification')); +$form->applyFilter('title', 'trim'); +$form->addHtmlEditor('content', get_lang('Content'), true, false, ['ToolbarSet' => 'Minimal']); +$form->addUrl( + 'expiration_link', + [$plugin->get_lang('ExpirationLink'), $plugin->get_lang('ExpirationLinkHelp')], + false, + ['placeholder' => 'https://'] +); +$form->addButtonSave(get_lang('Save')); + +if ($form->validate()) { + $values = $form->exportValues(); + + $notification + ->setContent($values['content']) + ->setExpirationLink($values['expiration_link']) + ->setCourse($course) + ->setHash(md5(uniqid())); + + $em->persist($notification); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('NotificationAdded'), 'success') + ); + + header('Location: '.api_get_course_url()); + exit; +} + +if ($notification) { + $form->setDefaults( + [ + 'content' => $notification->getContent(), + 'expiration_link' => $notification->getExpirationLink(), + ] + ); +} + +$template = new Template($plugin->get_title()); +$template->assign('header', $plugin->get_title()); + +if ($actionLinks) { + $template->assign('actions', Display::toolbarAction('course-home-notify-actions', ['', $actionLinks])); +} + +$template->assign('content', $form->returnForm()); +$template->display_one_col_template(); diff --git a/plugin/coursehomenotify/content.php b/plugin/coursehomenotify/content.php new file mode 100644 index 000000000..8de1b1159 --- /dev/null +++ b/plugin/coursehomenotify/content.php @@ -0,0 +1,52 @@ +get(CourseHomeNotifyPlugin::SETTING_ENABLED) +) { + api_not_allowed(true); +} + +$user = api_get_user_entity($userId); +$course = api_get_course_entity($courseId); +$hash = isset($_GET['hash']) ? Security::remove_XSS($_GET['hash']) : null; + +$em = Database::getManager(); +/** @var Notification $notification */ +$notification = $em + ->getRepository('ChamiloPluginBundle:CourseHomeNotify\Notification') + ->findOneBy(['course' => $course, 'hash' => $hash]); + +if (!$notification) { + api_not_allowed(true); +} + +$notificationUser = $em + ->getRepository('ChamiloPluginBundle:CourseHomeNotify\NotificationRelUser') + ->findOneBy(['notification' => $notification, 'user' => $user]); + +if (!$notificationUser) { + $notificationUser = new NotificationRelUser(); + $notificationUser + ->setUser($user) + ->setNotification($notification); + + $em->persist($notificationUser); + $em->flush(); +} + +header('Location: '.$notification->getExpirationLink()); diff --git a/plugin/coursehomenotify/install.php b/plugin/coursehomenotify/install.php new file mode 100644 index 000000000..d1304ee52 --- /dev/null +++ b/plugin/coursehomenotify/install.php @@ -0,0 +1,8 @@ +install(); diff --git a/plugin/coursehomenotify/lang/english.php b/plugin/coursehomenotify/lang/english.php new file mode 100644 index 000000000..a7f73251c --- /dev/null +++ b/plugin/coursehomenotify/lang/english.php @@ -0,0 +1,17 @@ +get_info(); diff --git a/plugin/coursehomenotify/uninstall.php b/plugin/coursehomenotify/uninstall.php new file mode 100644 index 000000000..038b7147e --- /dev/null +++ b/plugin/coursehomenotify/uninstall.php @@ -0,0 +1,8 @@ +uninstall(); diff --git a/plugin/courselegal/CourseLegalPlugin.php b/plugin/courselegal/CourseLegalPlugin.php new file mode 100644 index 000000000..f4bbb6be0 --- /dev/null +++ b/plugin/courselegal/CourseLegalPlugin.php @@ -0,0 +1,552 @@ + 'courselegal', + 'type' => 'text', + ], + ]; + + protected function __construct() + { + parent::__construct( + '0.1', + 'Julio Montoya', + [ + 'tool_enable' => 'boolean', + ] + ); + } + + /** + * @return CourseLegalPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @return string + */ + public function getTeacherLink() + { + $link = null; + if (api_is_allowed_to_edit()) { + $url = api_get_path(WEB_PLUGIN_PATH).'courselegal/start.php?'.api_get_cidreq(); + $link = Display::url( + $this->get_lang('CourseLegal'), + $url, + ['class' => 'btn'] + ); + } + + return $link; + } + + /** + * @param int $userId + * @param int $courseId + * @param int $sessionId + * + * @return array + */ + public function getUserAcceptedLegal($userId, $courseId, $sessionId) + { + $userId = intval($userId); + $courseId = intval($courseId); + $sessionId = intval($sessionId); + + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + $sql = "SELECT * + FROM $table + WHERE user_id = $userId AND c_id = $courseId AND session_id = $sessionId"; + $result = Database::query($sql); + $data = []; + if (Database::num_rows($result) > 0) { + $data = Database::fetch_array($result, 'ASSOC'); + } + + return $data; + } + + /** + * @param int $userId + * @param string $courseCode + * @param int $sessionId + * + * @return bool + */ + public function isUserAcceptedLegal($userId, $courseCode, $sessionId) + { + $courseInfo = api_get_course_info($courseCode); + $courseId = $courseInfo['real_id']; + $result = $this->getUserAcceptedLegal($userId, $courseId, $sessionId); + + if (!empty($result)) { + if ($result['mail_agreement'] == 1 && + $result['web_agreement'] == 1 + ) { + return true; + } + } + + return false; + } + + /** + * @param int $userId + * @param int $courseCode + * @param int $sessionId + * @param bool $sendEmail Optional. Indicate whether the mail must be sent. Default is true + * + * @return mixed + */ + public function saveUserLegal($userId, $courseCode, $sessionId, $sendEmail = true) + { + $courseInfo = api_get_course_info($courseCode); + $courseId = $courseInfo['real_id']; + $data = $this->getUserAcceptedLegal($userId, $courseId, $sessionId); + + $id = false; + if (empty($data)) { + $table = Database::get_main_table( + 'session_rel_course_rel_user_legal' + ); + $uniqueId = api_get_unique_id(); + $values = [ + 'user_id' => $userId, + 'c_id' => $courseId, + 'session_id' => $sessionId, + 'web_agreement' => 1, + 'web_agreement_date' => api_get_utc_datetime(), + 'mail_agreement_link' => $uniqueId, + ]; + $id = Database::insert($table, $values); + + if ($sendEmail) { + $this->sendMailLink($uniqueId, $userId, $courseId, $sessionId); + } + } + + return $id; + } + + /** + * @param int $userId + * @param int $courseId + * @param int $sessionId + */ + public function updateMailAgreementLink($userId, $courseId, $sessionId) + { + $data = $this->getUserAcceptedLegal($userId, $courseId, $sessionId); + if (!empty($data)) { + $table = Database::get_main_table( + 'session_rel_course_rel_user_legal' + ); + $uniqueId = api_get_unique_id(); + Database::update( + $table, + ['mail_agreement_link' => $uniqueId], + ['id = ? ' => [$data['id']]] + ); + $this->sendMailLink($uniqueId, $userId, $courseId, $sessionId); + } + } + + /** + * @param int $userId + * @param int $courseId + * @param int $sessionId + */ + public function deleteUserAgreement($userId, $courseId, $sessionId) + { + $data = $this->getUserAcceptedLegal($userId, $courseId, $sessionId); + if (!empty($data)) { + $table = Database::get_main_table( + 'session_rel_course_rel_user_legal' + ); + Database::delete( + $table, + ['id = ? ' => [$data['id']]] + ); + } + } + + /** + * @param string $uniqueId + * @param int $userId + * @param int $courseId + * @param int $sessionId + */ + public function sendMailLink($uniqueId, $userId, $courseId, $sessionId) + { + $courseInfo = api_get_course_info_by_id($courseId); + $courseCode = $courseInfo['code']; + + $url = api_get_path(WEB_CODE_PATH).'course_info/legal.php?web_agreement_link='.$uniqueId.'&course_code='.Security::remove_XSS($courseCode).'&session_id='.$sessionId; + $courseUrl = Display::url($url, $url); + $sessionInfo = api_get_session_info($sessionId); + $sesstionTitle = null; + + if (!empty($sessionInfo)) { + $sesstionTitle = ' ('.$sessionInfo['name'].')'; + } + + $courseTitle = $courseInfo['title'].$sesstionTitle; + + $subject = $this->get_lang("MailAgreement"); + $message = sprintf($this->get_lang("MailAgreementWasSentWithClickX"), $courseTitle, $courseUrl); + MessageManager::send_message_simple($userId, $subject, $message); + } + + /** + * @param string $link + * @param int $userId + * @param int $courseId + * @param int $sessionId + * + * @return bool + */ + public function saveUserMailLegal($link, $userId, $courseId, $sessionId) + { + $data = $this->getUserAcceptedLegal($userId, $courseId, $sessionId); + + if (empty($data)) { + return null; + } + + if ($data['mail_agreement_link'] == $link) { + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + $id = $data['id']; + $values = [ + 'mail_agreement' => 1, + 'mail_agreement_date' => api_get_utc_datetime(), + ]; + Database::update($table, $values, ['id = ?' => [$id]]); + } + } + + /** + * @param int $courseId + * @param int $sessionId + * @param string $filePath + */ + public function warnUsersByEmail($courseId, $sessionId, $filePath = null) + { + $courseInfo = api_get_course_info_by_id($courseId); + $courseCode = $courseInfo['code']; + + if (empty($sessionId)) { + $students = CourseManager::get_student_list_from_course_code($courseCode, false); + } else { + $students = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionId); + } + + $url = api_get_course_url($courseCode, $sessionId); + $url = Display::url($url, $url); + + $subject = $this->get_lang("AgreementUpdated"); + $message = sprintf($this->get_lang("AgreementWasUpdatedClickHere"), $url); + + $dataFile = []; + if (!empty($filePath)) { + $dataFile = [ + 'path' => $filePath, + 'filename' => basename($filePath), + ]; + $message = sprintf($this->get_lang("AgreementWasUpdatedClickHere"), $url)." \n"; + $message .= $this->get_lang("TheAgreementIsAttachedInThisEmail"); + } + + if (!empty($students)) { + foreach ($students as $student) { + $userInfo = api_get_user_info($student['user_id']); + api_mail_html( + $userInfo['complete_name'], + $userInfo['email'], + $subject, + $message, + null, + null, + null, + $dataFile + ); + //MessageManager::send_message_simple($student['user_id'], $subject, $message); + } + } + } + + /** + * @param int $courseId + * @param int $sessionId + * @param string $order + * + * @return array + */ + public function getUserAgreementList($courseId, $sessionId, $order = null) + { + $courseId = intval($courseId); + $sessionId = intval($sessionId); + + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + $userTable = Database::get_main_table(TABLE_MAIN_USER); + $sql = "SELECT * + FROM $table s INNER JOIN $userTable u + ON u.user_id = s.user_id + WHERE c_id = $courseId AND session_id = $sessionId "; + + if (!empty($order)) { + $sql .= $order; + } + $result = Database::query($sql); + $data = []; + if (Database::num_rows($result) > 0) { + $data = Database::store_result($result, 'ASSOC'); + } + + return $data; + } + + /** + * @param int $courseId + * @param int $sessionId + */ + public function removePreviousAgreements($courseId, $sessionId) + { + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + $sessionId = intval($sessionId); + $courseId = intval($courseId); + $sql = "DELETE FROM $table + WHERE c_id = '$courseId' AND session_id = $sessionId "; + Database::query($sql); + } + + /** + * @param array $values + * @param array $file $_FILES['uploaded_file'] + * @param bool $deleteFile + */ + public function save($values, $file = [], $deleteFile = false) + { + $table = Database::get_main_table('session_rel_course_legal'); + + $courseId = $values['c_id']; + $sessionId = $values['session_id']; + + $conditions = [ + 'c_id' => $courseId, + 'session_id' => $sessionId, + ]; + + $course = api_get_course_info_by_id($courseId); + + $legalData = $this->getData($courseId, $sessionId); + $coursePath = api_get_path(SYS_COURSE_PATH).$course['directory'].'/courselegal'; + $uploadResult = $coursePath.'/'.$legalData['filename']; + + if (!is_dir($coursePath)) { + mkdir($coursePath, api_get_permissions_for_new_directories()); + } + $uploadOk = process_uploaded_file($file, false); + $fileName = null; + + if ($uploadOk) { + $uploadResult = handle_uploaded_document( + $course, + $file, + $coursePath, + '/', + api_get_user_id(), + api_get_group_id(), + null, + false, + false, + false, + true + ); + + if ($uploadResult) { + $fileName = basename($uploadResult); + // Delete old one if exists. + if ($legalData) { + if (!empty($legalData['filename'])) { + $fileToDelete = $coursePath.'/'.$legalData['filename']; + if (file_exists($fileToDelete)) { + unlink($fileToDelete); + } + } + } + } + } + + $conditions['content'] = $values['content']; + $conditions['filename'] = $fileName; + + if (empty($legalData)) { + $id = Database::insert($table, $conditions); + } else { + $id = $legalData['id']; + + $updateParams = [ + 'content' => $values['content'], + ]; + + if (!empty($fileName)) { + $updateParams['filename'] = $fileName; + } + + Database::update( + $table, + $updateParams, + ['id = ? ' => $id] + ); + } + + if ($deleteFile) { + Database::update( + $table, + ['filename' => ''], + ['id = ? ' => $id] + ); + if (!empty($legalData['filename'])) { + $fileToDelete = $coursePath.'/'.$legalData['filename']; + if (file_exists($fileToDelete)) { + unlink($fileToDelete); + } + } + } + + if (isset($values['remove_previous_agreements']) && + !empty($values['remove_previous_agreements']) + ) { + $this->removePreviousAgreements($courseId, $sessionId); + } + + $warnUsers = isset($values['warn_users_by_email']) ? $values['warn_users_by_email'] : null; + + switch ($warnUsers) { + case '1': + // Nothing + break; + case '2': + // Send mail + $this->warnUsersByEmail($courseId, $sessionId); + break; + case '3': + // Send mail + attachment if exists. + if (!empty($legalData['filename'])) { + $this->warnUsersByEmail( + $courseId, + $sessionId, + $uploadResult + ); + } + break; + } + } + + /** + * @param int $courseId + * @param int $sessionId + * + * @return array|mixed + */ + public function getData($courseId, $sessionId) + { + $table = Database::get_main_table('session_rel_course_legal'); + $conditions = [ + 'c_id = ? AND session_id = ? ' => [ + $courseId, + $sessionId, + ], + ]; + + $result = Database::select('*', $table, ['where' => $conditions]); + $legalData = isset($result) && !empty($result) ? current($result) : []; + + return $legalData; + } + + /** + * @param int $courseId + * @param int $sessionId + * + * @return string + */ + public function getCurrentFile($courseId, $sessionId) + { + $data = $this->getData($courseId, $sessionId); + + if (isset($data['filename']) && !empty($data['filename'])) { + $course = api_get_course_info_by_id($courseId); + + $coursePath = api_get_path(SYS_COURSE_PATH).$course['directory'].'/courselegal'; + $file = $coursePath.'/'.$data['filename']; + + if (file_exists($file)) { + return Display::url( + $data['filename'], + api_get_path(WEB_COURSE_PATH).$course['directory'].'/courselegal/'.$data['filename'], + ['target' => '_blank'] + ); + } + } + } + + public function install() + { + $table = Database::get_main_table('session_rel_course_legal'); + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int PRIMARY KEY AUTO_INCREMENT, + c_id int, + session_id int, + content text, + filename varchar(255) + )"; + Database::query($sql); + + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + + $sql = "CREATE TABLE IF NOT EXISTS $table ( + id int PRIMARY KEY AUTO_INCREMENT, + user_id int, + c_id int, + session_id int, + web_agreement varchar(255), + web_agreement_date datetime, + mail_agreement varchar(255), + mail_agreement_date datetime, + mail_agreement_link varchar(255) + )"; + Database::query($sql); + + // Installing course settings + $this->install_course_fields_in_all_courses(false); + } + + public function uninstall() + { + $table = Database::get_main_table('session_rel_course_legal'); + $sql = "DROP TABLE $table "; + Database::query($sql); + + $table = Database::get_main_table('session_rel_course_rel_user_legal'); + $sql = "DROP TABLE $table "; + Database::query($sql); + + // Deleting course settings + $this->uninstall_course_fields_in_all_courses($this->course_settings); + } +} diff --git a/plugin/courselegal/README.md b/plugin/courselegal/README.md new file mode 100644 index 000000000..379049ba5 --- /dev/null +++ b/plugin/courselegal/README.md @@ -0,0 +1,25 @@ +Installation +============ + +1. Enabled the plugin from the list of plugins. +2. Click "Configure" once the plugin was enabled. +3. Select tool_enable = Yes and save. +4. Go into a *course* (not course session) and enter the Settings tool. +5. In the "Course access" section select "Enable legal terms" and + change the course visibility to "Private" and save. +6. Go to a course or a course in a session a new button "Legal" will appear + in the course home. +7. Click the button "Legal". Fill the form with the content and a file you want +to be shown to the students. There are other options in the form, like: + - Send an email to all users. + - Delete all previous agreements + - Delete the file. + + The file are saved in courses/XX/courselegal/ + +8. Once the form was saved, a new form will appear for the registered users. + The student has to accept the form. + When the form is accepted an email will be sent to the user via email and + then Chamilo message tool +9. The user has to click in the URL. Once the user clicked to the URL the user + will have access to the course. diff --git a/plugin/courselegal/config.php b/plugin/courselegal/config.php new file mode 100644 index 000000000..46e612525 --- /dev/null +++ b/plugin/courselegal/config.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/courselegal/lang/english.php b/plugin/courselegal/lang/english.php new file mode 100644 index 000000000..61062fc34 --- /dev/null +++ b/plugin/courselegal/lang/english.php @@ -0,0 +1,23 @@ + %s'; +$strings['AgreementUpdated'] = 'Agreement updated'; +$strings['AgreementWasUpdatedClickHere'] = 'The agreement was updated. Click here: %s'; +$strings['YouNeedToConfirmYourAgreementCheckYourEmail'] = 'You need to confirm that you agree. Please check your email.'; +$strings['DeleteFile'] = 'Delete the file attachment'; +$strings['RemoveAllUserAgreements'] = 'Remove all previous user agreements'; +$strings['WarnAllUsersByEmail'] = 'Notify all users by email'; +$strings['WarnAllUsersByEmailAndSendAttachment'] = 'Notify all users by email and send them the attachment.'; +$strings['ReSendMailAgreementLink'] = 'Send e-mail agreement link again'; +$strings['SendOnlyWarning'] = 'Send only notification'; +$strings['SendAgreementFile'] = 'Send notification and attach the agreement'; +$strings['NoSendWarning'] = 'Do not send notification'; +$strings['TheAgreementIsAttachedInThisEmail'] = 'A copy of the agreement document is attached to this email'; diff --git a/plugin/courselegal/lang/french.php b/plugin/courselegal/lang/french.php new file mode 100644 index 000000000..d42bbaec3 --- /dev/null +++ b/plugin/courselegal/lang/french.php @@ -0,0 +1,23 @@ + %s'; +$strings['AgreementUpdated'] = 'Termes mis à jour'; +$strings['AgreementWasUpdatedClickHere'] = 'Les termes d\'utilisation ont été mis à jour. Cliquez ici: %s'; +$strings['YouNeedToConfirmYourAgreementCheckYourEmail'] = 'Veuillez confirmer votre accord. Vérifiez votre e-mail.'; +$strings['DeleteFile'] = 'Éliminez la pièce jointe'; +$strings['RemoveAllUserAgreements'] = 'Supprimer tous les accords utilisateurs antérieurs'; +$strings['WarnAllUsersByEmail'] = 'Avertir tous les utilisateurs par mail'; +$strings['WarnAllUsersByEmailAndSendAttachment'] = 'Avertir tous les utilisateurs par mail avec la pièce jointe.'; +$strings['ReSendMailAgreementLink'] = 'Envoyer le lien d\'accord par mail de nouveau'; +$strings['SendOnlyWarning'] = 'Envoyer seulement l\'avertissement'; +$strings['SendAgreementFile'] = 'Envoyer à nouveau l\'avertissement avec la pièce jointe'; +$strings['NoSendWarning'] = 'Ne pas envoyer l\'avertissement'; +$strings['TheAgreementIsAttachedInThisEmail'] = 'Une copie du document d\'accord est joint à cet e-mail'; diff --git a/plugin/courselegal/lang/spanish.php b/plugin/courselegal/lang/spanish.php new file mode 100644 index 000000000..ffbee3c33 --- /dev/null +++ b/plugin/courselegal/lang/spanish.php @@ -0,0 +1,23 @@ + %s'; +$strings['AgreementUpdated'] = 'Acuerdo actualizado'; +$strings['AgreementWasUpdatedClickHere'] = 'El acuerdo fue actualizado. Haga clic aquí: %s'; +$strings['YouNeedToConfirmYourAgreementCheckYourEmail'] = 'Usted necesita confirmar que está de acuerdo. Revise su correo electrónico, por favor'; +$strings['DeleteFile'] = 'Eliminar archivo adjuntado'; +$strings['RemoveAllUserAgreements'] = 'Eliminar todos los acuerdos de usuarios anteriores'; +$strings['WarnAllUsersByEmail'] = 'Notificar a todos los usuarios por corro electrónico'; +$strings['WarnAllUsersByEmailAndSendAttachment'] = 'Notificar a todos los usuarios por correo electrónico y enviarles el acuerdo adjuntado'; +$strings['ReSendMailAgreementLink'] = 'Enviar un correo electrónico con el enlace al acuerdo de nuevo.'; +$strings['SendOnlyWarning'] = 'Enviar solamente notificación'; +$strings['SendAgreementFile'] = 'Enviar notificación y adjuntar el acuerdo'; +$strings['NoSendWarning'] = 'No enviar notificación'; +$strings['TheAgreementIsAttachedInThisEmail'] = 'Una copia de este documento de acuerdo es adjuntado a este correo electrónico.'; diff --git a/plugin/courselegal/plugin.php b/plugin/courselegal/plugin.php new file mode 100644 index 000000000..36bf3dfb7 --- /dev/null +++ b/plugin/courselegal/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/courselegal/start.php b/plugin/courselegal/start.php new file mode 100644 index 000000000..6b7631915 --- /dev/null +++ b/plugin/courselegal/start.php @@ -0,0 +1,64 @@ +addElement('header', $legal->get_lang('CourseLegal')); +$form->addElement('hidden', 'session_id', $sessionId); +$form->addElement('hidden', 'c_id', $courseId); +$form->addHtmlEditor( + 'content', + get_lang('Text'), + true, + false, + ['ToolbarSet' => 'TermsAndConditions'] +); +$form->addElement('file', 'uploaded_file', get_lang('File')); +$file = $legal->getCurrentFile($courseId, $sessionId); + +if (!empty($file)) { + $form->addElement('label', get_lang('File'), $file); +} + +$form->addElement('checkbox', 'delete_file', null, $legal->get_lang('DeleteFile')); +$form->addElement('checkbox', 'remove_previous_agreements', null, $legal->get_lang('RemoveAllUserAgreements')); +$form->addElement('radio', 'warn_users_by_email', null, $legal->get_lang('NoSendWarning'), 1); +$form->addElement('radio', 'warn_users_by_email', $legal->get_lang('WarnAllUsersByEmail'), $legal->get_lang('SendOnlyWarning'), 2); +$form->addElement('radio', 'warn_users_by_email', null, $legal->get_lang('SendAgreementFile'), 3); +$form->addButtonSave(get_lang('Save')); +$defaults = $legal->getData($courseId, $sessionId); +$defaults['warn_users_by_email'] = 1; +$form->setDefaults($defaults); + +if ($form->validate()) { + $values = $form->getSubmitValues(); + $file = isset($_FILES['uploaded_file']) ? $_FILES['uploaded_file'] : []; + $deleteFile = isset($values['delete_file']) ? $values['delete_file'] : false; + $legal->save($values, $file, $deleteFile); + header('Location: '.$url); + exit; +} +Display::display_header($legal->get_lang('CourseLegal')); +$url = api_get_path(WEB_PLUGIN_PATH).'courselegal/user_list.php?'.api_get_cidreq(); +$link = Display::url(Display::return_icon('user.png', get_lang('UserList')), $url); +echo '
    '.$link.'
    '; +$form->display(); + +Display::display_footer(); diff --git a/plugin/courselegal/user_list.php b/plugin/courselegal/user_list.php new file mode 100644 index 000000000..63b276207 --- /dev/null +++ b/plugin/courselegal/user_list.php @@ -0,0 +1,81 @@ +updateMailAgreementLink($_GET['user_id'], $courseId, $sessionId); + header('Location: '.$url); + exit; + } + break; + case 'delete': + if (isset($_GET['user_id'])) { + $legal->deleteUserAgreement($_GET['user_id'], $courseId, $sessionId); + header('Location: '.$url); + exit; + } + break; +} + +$order = " ORDER BY firstname, lastname"; +$userList = $legal->getUserAgreementList($courseId, $sessionId, $order); +$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']); +$table->setHeaderContents(0, 0, get_lang('User')); +$table->setHeaderContents(0, 1, $legal->get_lang('WebAgreement')); +$table->setHeaderContents(0, 2, $legal->get_lang('MailAgreement')); +$table->setHeaderContents(0, 3, $legal->get_lang('Actions')); +$row = 1; + +$pluginPath = api_get_path(WEB_PLUGIN_PATH).'courselegal/'; +if (!empty($userList)) { + foreach ($userList as $user) { + $userName = api_get_person_name($user['firstname'], $user['lastname']); + + $webDate = !empty($user['web_agreement_date']) ? api_get_local_time($user['web_agreement_date']) : '-'; + $mailDate = !empty($user['mail_agreement_date']) ? api_get_local_time($user['mail_agreement_date']) : '-'; + $url = $pluginPath.'user_list.php?action=resend&user_id='.$user['user_id'].'&'.api_get_cidreq(); + $link = Display::url( + Display::return_icon('inbox.png', $legal->get_lang('ReSendMailAgreementLink')), + $url + ); + + $deleteLink = Display::url( + Display::return_icon('delete.png', $legal->get_lang('Delete')), + $pluginPath.'user_list.php?action=delete&user_id='.$user['user_id'].'&'.api_get_cidreq() + ); + + $table->setCellContents($row, 0, $userName); + $table->setCellContents($row, 1, $webDate); + $table->setCellContents($row, 2, $mailDate); + $table->setCellContents($row, 3, $link.' '.$deleteLink); + $row++; + } +} +$url = $pluginPath.'start.php?'.api_get_cidreq(); + +$interbreadcrumb[] = ["url" => $url, "name" => $legal->get_lang('CourseLegal')]; +Display::display_header($legal->get_lang('UserList')); + +$table->display(); + +Display::display_footer(); diff --git a/plugin/createdrupaluser/README.md b/plugin/createdrupaluser/README.md new file mode 100644 index 000000000..3ee94fff1 --- /dev/null +++ b/plugin/createdrupaluser/README.md @@ -0,0 +1,28 @@ +Create Drupal user plugin +========================= + +This plugin creates a user on a Drupal portal when a user is registered in +Chamilo. + +This uses the Hook mechanism available to Chamilo plugins: when enabling this +plugin, the HookCreateDrupalUser is automatically added to the hooks stack, and +the UserManager::create_user() method calls the HookCreateUser hook and notifies +it, resulting in the plugin code to be executed. + +The Drupal portal settings must be configured in the configuration panel for the +plugin. A SOAP call is then initiated inside the plugin code, that will use the +Drupal's addUser() web service. See src/HookCreateDrupalUser.php for more info +on the call parameters. + +After calling the web service and receiving a positive answer, Chamilo stores +the remote (Drupal) user ID inside the extra_field_values table, as field name +"drupal_user_id". This later serves for updates and other synchronisation +purposes. + +Extending +--------- + +Other plugins could easily be created by copying this one and modifying the +class names and web services to call. Simply review every variable coined with +the "drupal" name and update according to your own portal. + diff --git a/plugin/createdrupaluser/config.php b/plugin/createdrupaluser/config.php new file mode 100644 index 000000000..420022974 --- /dev/null +++ b/plugin/createdrupaluser/config.php @@ -0,0 +1,10 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +require_once api_get_path(SYS_PATH).'main/inc/global.inc.php'; diff --git a/plugin/createdrupaluser/index.php b/plugin/createdrupaluser/index.php new file mode 100644 index 000000000..f8f4cac73 --- /dev/null +++ b/plugin/createdrupaluser/index.php @@ -0,0 +1,10 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +require_once __DIR__.'/config.php'; diff --git a/plugin/createdrupaluser/install.php b/plugin/createdrupaluser/install.php new file mode 100644 index 000000000..09536f98a --- /dev/null +++ b/plugin/createdrupaluser/install.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +require_once __DIR__.'/config.php'; + +CreateDrupalUser::create()->install(); diff --git a/plugin/createdrupaluser/lang/english.php b/plugin/createdrupaluser/lang/english.php new file mode 100644 index 000000000..918006910 --- /dev/null +++ b/plugin/createdrupaluser/lang/english.php @@ -0,0 +1,16 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +$strings['plugin_title'] = 'Create Drupal users'; +$strings['plugin_comment'] = 'This plugin creates users in an associated Drupal website when a user is created in Chamilo LMS. This requires the "chamilo" module to be installed and configured on the Drupal side.'; + +$strings['drupal_domain'] = 'Drupal website URL'; +$strings['drupal_domain_help'] = 'The server domain name should be written with a trailing slash and with the protocol, e.g. http://www.example.com/'; + +$strings['DruaplUserId'] = 'Drupal user ID'; diff --git a/plugin/createdrupaluser/lang/spanish.php b/plugin/createdrupaluser/lang/spanish.php new file mode 100644 index 000000000..0c2b51eac --- /dev/null +++ b/plugin/createdrupaluser/lang/spanish.php @@ -0,0 +1,16 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +$strings['plugin_title'] = 'Crear usuario Drupal'; +$strings['plugin_comment'] = 'Este plugin permite crear usuarios en un sitio web Drupal cuando un usuario es creado en Chamilo LMS. Este plugin requiere que el módulo "chamilo" de Drupal esté instalado y configurado en el sitio Drupal.'; + +$strings['drupal_domain'] = 'URL del sitio web Drupal'; +$strings['drupal_domain_help'] = 'La dirección del servidor debe escribirse con el protocolo al comienzo y con la barra al final, por ejemplo http://www.example.com/'; + +$strings['DrupalUserId'] = 'ID de usuario Drupal'; diff --git a/plugin/createdrupaluser/plugin.php b/plugin/createdrupaluser/plugin.php new file mode 100644 index 000000000..071fe982c --- /dev/null +++ b/plugin/createdrupaluser/plugin.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +require_once __DIR__.'/config.php'; + +$plugin_info = CreateDrupalUser::create()->get_info(); diff --git a/plugin/createdrupaluser/src/CreateDrupalUser.php b/plugin/createdrupaluser/src/CreateDrupalUser.php new file mode 100644 index 000000000..bf7a14a6f --- /dev/null +++ b/plugin/createdrupaluser/src/CreateDrupalUser.php @@ -0,0 +1,156 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +class CreateDrupalUser extends Plugin implements HookPluginInterface +{ + public const EXTRAFIELD_VARIABLE_NAME = 'drupal_user_id'; + + /** + * Class constructor. + */ + protected function __construct() + { + $parameters = [ + 'drupal_domain' => 'text', + ]; + + parent::__construct('1.0', 'Angel Fernando Quiroz Campos', $parameters); + } + + /** + * Instance the plugin. + * + * @staticvar null $result + * + * @return CreateDrupalUser + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Install the plugin. + */ + public function install() + { + $this->createExtraField(); + $this->installHook(); + } + + /** + * Uninstall the plugin. + */ + public function uninstall() + { + $this->uninstallHook(); + $this->deleteExtraField(); + } + + /** + * Install the Create User hook. + */ + public function installHook() + { + /** @var HookCreateDrupalUser $observer */ + $observer = HookCreateDrupalUser::create(); + HookCreateUser::create()->attach($observer); + } + + /** + * Uninstall the Create User hook. + */ + public function uninstallHook() + { + /** @var HookCreateDrupalUser $observer */ + $observer = HookCreateDrupalUser::create(); + $event = HookCreateUser::create(); + + if ($event) { + $event->detach($observer); + } + } + + /** + * Get the drupal_user_id extra field information. + * + * @return array The info + */ + private function getExtraFieldInfo() + { + $extraField = new ExtraField('user'); + $extraFieldHandler = $extraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_VARIABLE_NAME + ); + + return $extraFieldHandler; + } + + /** + * Create the drupal_user_id when it not exists. + */ + private function createExtraField() + { + $extraFieldExists = $this->getExtraFieldInfo() !== false; + + if (!$extraFieldExists) { + $extraField = new ExtraField('user'); + $extraField->save( + [ + 'field_type' => ExtraField::FIELD_TYPE_INTEGER, + 'variable' => self::EXTRAFIELD_VARIABLE_NAME, + 'display_text' => get_plugin_lang('DrupalUserId', 'CreateDrupalUser'), + 'default_value' => null, + 'field_order' => null, + 'visible_to_self' => false, + 'changeable' => false, + 'filter' => null, + ] + ); + } + } + + /** + * Delete the drupal_user_id and values. + */ + private function deleteExtraField() + { + $extraFieldInfo = $this->getExtraFieldInfo(); + $extraFieldExists = $extraFieldInfo !== false; + + if ($extraFieldExists) { + $extraField = new ExtraField('user'); + $extraField->delete($extraFieldInfo['id']); + } + } + + /* + public function notifyDocumentAction(HookDocumentActionEventInterface $hook) + { + $data = $hook->getEventData(); + if ($data['type'] === HOOK_EVENT_TYPE_PRE) { + $data['actions'][] = Display::return_icon('edit.png'); + } + + return $data; + } + + public function notifyDocumentItemAction(HookDocumentItemActionEventInterface $hook) + { + $data = $hook->getEventData(); + if ($data['type'] === HOOK_EVENT_TYPE_PRE) { + $data['actions'][] = $data['id'].' - '.$data['title']; + } + + return $data; + }*/ +} diff --git a/plugin/createdrupaluser/src/HookCreateDrupalUser.php b/plugin/createdrupaluser/src/HookCreateDrupalUser.php new file mode 100644 index 000000000..fcf8761d5 --- /dev/null +++ b/plugin/createdrupaluser/src/HookCreateDrupalUser.php @@ -0,0 +1,78 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +class HookCreateDrupalUser extends HookObserver implements HookCreateUserObserverInterface +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct( + 'plugin/createdrupaluser/src/CreateDrupalUser.php', + 'drupaluser' + ); + } + + /** + * Create a Drupal user when the Chamilo user is registered. + * + * @param HookCreateUserEventInterface $hook The hook + */ + public function hookCreateUser(HookCreateUserEventInterface $hook) + { + $data = $hook->getEventData(); + + $drupalDomain = CreateDrupalUser::create()->get('drupal_domain'); + $drupalDomain = rtrim($drupalDomain, '/').'/'; + + if ($data['type'] === HOOK_EVENT_TYPE_POST) { + $return = $data['return']; + $originalPassword = $data['originalPassword']; + + $userInfo = api_get_user_info($return); + $fields = [ + 'name' => $userInfo['username'], + 'pass' => $originalPassword, + 'mail' => $userInfo['email'], + 'status' => 1, + 'init' => $userInfo['email'], + ]; + + $extraFields = [ + 'first_name' => $userInfo['firstname'], + 'last_name' => $userInfo['lastname'], + ]; + + $options = [ + 'location' => $drupalDomain.'sites/all/modules/chamilo/soap.php?wsdl', + 'uri' => $drupalDomain, + ]; + + $client = new SoapClient(null, $options); + $drupalUserId = false; + + if (isset($_SESSION['ws_drupal_user_id'])) { + $drupalUserId = $_SESSION['ws_drupal_user_id']; + + return true; + } + + if ($drupalUserId === false) { + $drupalUserId = $client->addUser($fields, $extraFields); + } + + if ($drupalUserId !== false) { + UserManager::update_extra_field_value($return, 'drupal_user_id', $drupalUserId); + } + } + } +} diff --git a/plugin/createdrupaluser/uninstall.php b/plugin/createdrupaluser/uninstall.php new file mode 100644 index 000000000..b83b73721 --- /dev/null +++ b/plugin/createdrupaluser/uninstall.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.createDrupalUser + */ +require_once __DIR__.'/config.php'; + +CreateDrupalUser::create()->uninstall(); diff --git a/plugin/customcertificate/README.md b/plugin/customcertificate/README.md new file mode 100644 index 000000000..477533795 --- /dev/null +++ b/plugin/customcertificate/README.md @@ -0,0 +1,26 @@ +CustomCertificate plugin +=============== +Este plugin da la posibilidad al administrador de disponer de una herramienta de certificados alternativa + a la que tiene por defecto la plataforma Chamilo. + +**Instrucciones de puesta en funcionamiento** + +- Habilitar el plugin en la administración de Chamilo. +- Indicar 'menu_administrator' en la configuración de la región del plugin. + +**accesos a la herramienta** + +- Desde la pantalla de Administración para configurar el certificado por defecto. +- Desde las herramientas del curso, para la configuración del diploma especifico. + +**Importante a tener en cuenta** + +Por defecto los certificados utilizados serán los de la plataforma chamilo. Para habilitar el certificado alternativo +en un curso se debe entrar en la configuración del curso y habilitar en la pestaña de "certificados personalizado" la +casilla de verificación de "Habilitar en el curso el certificado alternativo". +Si se desea usar el certificado por defecto se deberá mostrar la segunda casilla de verificación. + + +Credits +------- +Contributed by [Nosolored](https://www.nosolored.com/). \ No newline at end of file diff --git a/plugin/customcertificate/config.php b/plugin/customcertificate/config.php new file mode 100644 index 000000000..c27e28363 --- /dev/null +++ b/plugin/customcertificate/config.php @@ -0,0 +1,11 @@ + + */ +require_once __DIR__.'/../../main/inc/global.inc.php'; diff --git a/plugin/customcertificate/database.php b/plugin/customcertificate/database.php new file mode 100644 index 000000000..a6c93457d --- /dev/null +++ b/plugin/customcertificate/database.php @@ -0,0 +1,68 @@ +getConnection(); +$platform = $connection->getDatabasePlatform(); + +if ($pluginSchema->hasTable(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE)) { + return; +} + +//Create tables +$certificateTable = $pluginSchema->createTable(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE); +$certificateTable->addColumn('id', Type::INTEGER, ['autoincrement' => true, 'unsigned' => true]); +$certificateTable->addColumn('access_url_id', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('c_id', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('session_id', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('content_course', Type::TEXT); +$certificateTable->addColumn('contents_type', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('contents', Type::TEXT); +$certificateTable->addColumn('date_change', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('date_start', Type::DATETIME); +$certificateTable->addColumn('date_end', Type::DATETIME); +$certificateTable->addColumn('type_date_expediction', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('place', Type::STRING); +$certificateTable->addColumn('day', Type::STRING, ['notnull' => false]); +$certificateTable->addColumn('month', Type::STRING, ['notnull' => false]); +$certificateTable->addColumn('year', Type::STRING, ['notnull' => false]); +$certificateTable->addColumn('logo_left', Type::STRING); +$certificateTable->addColumn('logo_center', Type::STRING); +$certificateTable->addColumn('logo_right', Type::STRING); +$certificateTable->addColumn('seal', Type::STRING); +$certificateTable->addColumn('signature1', Type::STRING); +$certificateTable->addColumn('signature2', Type::STRING); +$certificateTable->addColumn('signature3', Type::STRING); +$certificateTable->addColumn('signature4', Type::STRING); +$certificateTable->addColumn('signature_text1', Type::STRING); +$certificateTable->addColumn('signature_text2', Type::STRING); +$certificateTable->addColumn('signature_text3', Type::STRING); +$certificateTable->addColumn('signature_text4', Type::STRING); +$certificateTable->addColumn('background', Type::STRING); +$certificateTable->addColumn('margin_left', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('margin_right', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addColumn('certificate_default', Type::INTEGER, ['unsigned' => true]); +$certificateTable->addIndex(['c_id', 'session_id']); +$certificateTable->setPrimaryKey(['id']); + +$queries = $pluginSchema->toSql($platform); + +foreach ($queries as $query) { + Database::query($query); +} diff --git a/plugin/customcertificate/index.php b/plugin/customcertificate/index.php new file mode 100644 index 000000000..e70cd86d1 --- /dev/null +++ b/plugin/customcertificate/index.php @@ -0,0 +1,3 @@ +install(); diff --git a/plugin/customcertificate/lang/english.php b/plugin/customcertificate/lang/english.php new file mode 100644 index 000000000..a3b2a4266 --- /dev/null +++ b/plugin/customcertificate/lang/english.php @@ -0,0 +1,77 @@ + "Contents"'; +$strings['ContentsIndexLearnpath'] = "Use learnpath index"; +$strings['ContentsCustom'] = "Use custom content"; +$strings['ContentsHide'] = "No show contents"; +$strings['Dates'] = "Dates"; +$strings['CourseDeliveryDates'] = "Course delivery dates"; +$strings['Custom'] = "Custom"; +$strings['UseDateSessionAccess'] = "Use access dates to the session"; +$strings['ExpectionPlace'] = "Expection place"; +$strings['DateExpediction'] = "Expediction date"; +$strings['UseDateEndAccessSession'] = "Use end date of session access"; +$strings['UseDateDownloadCertificate'] = "Use certificate download date"; +$strings['UseDateGenerationCertificate'] = "Use certificate generation date"; +$strings['UseCustomDate'] = "Use custom date"; +$strings['LogosSeal'] = "Logos / Seals"; +$strings['LogoLeft'] = "Logo left"; +$strings['LogoCenter'] = "Logo center"; +$strings['LogoRight'] = "Logo right"; +$strings['Seal'] = "Seal"; +$strings['Signature1'] = "Signature 1"; +$strings['Signature2'] = "Signature 2"; +$strings['Signature3'] = "Signature 3"; +$strings['Signature4'] = "Signature 4"; +$strings['SignatureText1'] = "Signature text 1"; +$strings['SignatureText2'] = "Signature text 2"; +$strings['SignatureText3'] = "Signature text 3"; +$strings['SignatureText4'] = "Signature text 4"; +$strings['OtherOptions'] = "Others options"; +$strings['MarginRight'] = "Margin right"; +$strings['MarginLeft'] = "Margin left"; +$strings['SetDefaultTemplate'] = "Set template by default"; +$strings['MessageDefaultTemplate'] = "Save this default customization for courses and sessions without + defined certificates"; +$strings['None'] = "None"; +$strings['ErrorTemplateCertificate'] = "There is no template defined for the certificate. + There is no template by default."; +$strings['DateStartEnd'] = "With Start date and End date: "; +$strings['ExpedictionIn'] = "Expediction in"; +$strings['Signatures'] = "Signatures"; +$strings['BackgroundCertificate'] = "Background image of the certificate"; +$strings['Background'] = "Background"; +$strings['CertificateSetting'] = "Certificate setting"; +$strings['ToolDisabledCourse'] = "Tool disabled in course setting"; +$strings['ToolUseDefaultSettingCourse'] = "Tool configured to use the default certificate.
    + You can edit it from the Administration screen -> Plugins -> Custom Certificate.
    + Or if you want you can disable the option to use certificate by default in the plugin configuration in the course"; +$strings['CertificateSettingDefault'] = "Default certificate settings"; +$strings['InfoFromDefaultCertificate'] = "The content of the certificate is based on the default certificate. + The modifications you make will not affect the default certificate."; +$strings['to'] = " to "; +$strings['formatDownloadDate'] = " to %sth %s, %s"; +$strings['PrintCertificate'] = "Print certificate"; +$strings['QuestionDelete'] = "Do you want to delete the specific diploma and use the default certificate?"; +$strings['SuccessDelete'] = "Successfully deleted"; +$strings['ProblemDelete'] = "Problem deleting the certificate"; +$strings['OnlyCustomCertificates'] = "Only courses with a personalized certificate are exported"; diff --git a/plugin/customcertificate/lang/spanish.php b/plugin/customcertificate/lang/spanish.php new file mode 100644 index 000000000..27b05b74e --- /dev/null +++ b/plugin/customcertificate/lang/spanish.php @@ -0,0 +1,79 @@ + "Contenidos"'; +$strings['ContentsIndexLearnpath'] = "Usar indice de lecciones"; +$strings['ContentsCustom'] = "Usar contenido personalizado"; +$strings['ContentsHide'] = "No mostrar contenidos"; +$strings['Dates'] = "Fechas"; +$strings['CourseDeliveryDates'] = "Fechas de impartición del curso"; +$strings['Custom'] = "Personalizado"; +$strings['UseDateSessionAccess'] = "Usar fechas de acceso a la sesión"; +$strings['ExpectionPlace'] = "Lugar expedición"; +$strings['DateExpediction'] = "Fecha expedición"; +$strings['UseDateEndAccessSession'] = "Usar fecha fin de acceso de la sesión"; +$strings['UseDateDownloadCertificate'] = "Usar fecha de descarga del certificado"; +$strings['UseDateGenerationCertificate'] = "Usar fecha de generación del certificado"; +$strings['UseCustomDate'] = "Usar fechas personalizadas"; +$strings['LogosSeal'] = "Logos / Sellos"; +$strings['LogoLeft'] = "Logo izquierda"; +$strings['LogoCenter'] = "Logo central"; +$strings['LogoRight'] = "Logo derecha"; +$strings['Seal'] = "Sello"; +$strings['Signature1'] = "Firma 1"; +$strings['Signature2'] = "Firma 2"; +$strings['Signature3'] = "Firma 3"; +$strings['Signature4'] = "Firma 4"; +$strings['SignatureText1'] = "Texto firma 1"; +$strings['SignatureText2'] = "Texto firma 2"; +$strings['SignatureText3'] = "Texto firma 3"; +$strings['SignatureText4'] = "Texto firma 4"; +$strings['OtherOptions'] = "Otras opciones"; +$strings['MarginRight'] = "Margen derecho"; +$strings['MarginLeft'] = "Margen izquierdo"; +$strings['SetDefaultTemplate'] = "Establecer plantilla por defecto"; +$strings['MessageDefaultTemplate'] = "Guardar esta personalización por defecto para cursos y + sesiones sin certificados definidos"; +$strings['None'] = "Ninguno"; +$strings['ErrorTemplateCertificate'] = "No hay una plantilla definida para el certificado. + No existe plantilla por defecto."; +$strings['DateStartEnd'] = "Con Fecha de inicio y Fecha fin: "; +$strings['ExpedictionIn'] = "Expedido en"; +$strings['Signatures'] = "Firmas"; +$strings['BackgroundCertificate'] = "Imagen de fondo del certificado"; +$strings['Background'] = "Fondo"; +$strings['CertificateSetting'] = "Configuración Certificado"; +$strings['ToolDisabledCourse'] = "Herramienta desabilitada en el curso"; +$strings['ToolUseDefaultSettingCourse'] = "Herramienta configurada para usar el certificado por defecto.
    + Podrá editarlo desde la pantalla de Administración -> Plugins -> Certificado personalizado.
    + O si lo desea puede desactivar la opción de usar certificado por defecto en la configuración del + plugin en el curso"; +$strings['CertificateSettingDefault'] = "Configuración del certificado por defecto"; +$strings['InfoFromDefaultCertificate'] = "El contenido del certificado está basado en el certificado por defecto. + Las modificaciones que realice no afectará al certificado por defecto."; +$strings['to'] = " a "; +$strings['formatDownloadDate'] = " a %s de %s de %s"; +$strings['MessageUpdate'] = "El proceso de actualización ha terminado"; +$strings['PrintCertificate'] = "Imprimir certificado"; +$strings['QuestionDelete'] = "¿Desea eliminar el diploma específico y volver a usar el certificado por defecto?"; +$strings['SuccessDelete'] = "Borrado con éxito"; +$strings['ProblemDelete'] = "Hubo un problema al borrar el certificado"; +$strings['OnlyCustomCertificates'] = "Solo se exportan los diplomas de cursos con certificado personalizado"; diff --git a/plugin/customcertificate/plugin.php b/plugin/customcertificate/plugin.php new file mode 100644 index 000000000..a4797cc9f --- /dev/null +++ b/plugin/customcertificate/plugin.php @@ -0,0 +1,16 @@ +Plugins). + * + * @package chamilo.plugin.customcertificate + */ + +/** + * Plugin details (must be present). + */ +require_once __DIR__.'/config.php'; +$plugin_info = CustomCertificatePlugin::create()->get_info(); diff --git a/plugin/customcertificate/resources/css/certificate.css b/plugin/customcertificate/resources/css/certificate.css new file mode 100644 index 000000000..912df0948 --- /dev/null +++ b/plugin/customcertificate/resources/css/certificate.css @@ -0,0 +1,11 @@ +body {background-repeat:no-repeat; margin:0; padding: 0;} +td { font-family:arial, sans-serif; font-size:18px; } +.logo {height: 150px; vertical-align: middle; } +.seals { font-size: 16px; text-align:center;} +.logo-seals { height: 100px; text-align:center; vertical-align: middle;} +@page caraA { height: 210mm; margin:0; padding:0; page-break-before:always;} +.caraA {box-decoration-break: slice; page: caraA} +@page caraB { background:#FFFFFF; } +.caraB {box-decoration-break: slice; page: caraB} +.contents-learnpath td {background: #FFFFFF; font-size: 12px; height: 160mm; vertical-align: top;} +ol {margin: 0; padding:0;} diff --git a/plugin/customcertificate/resources/css/form.css b/plugin/customcertificate/resources/css/form.css new file mode 100644 index 000000000..7abb879fd --- /dev/null +++ b/plugin/customcertificate/resources/css/form.css @@ -0,0 +1,18 @@ +.form-control-cert { + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + color: #555; + font-size: 14px; + line-height: 1.42857143; + padding: 6px 12px; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; +} +.certificado-text-label { + font-weight: 700; + margin: 0 10px 0 10px; +} +input:disabled { background: #eee; } +label { font-size: 13px; } \ No newline at end of file diff --git a/plugin/customcertificate/resources/img/22/customcertificate.png b/plugin/customcertificate/resources/img/22/customcertificate.png new file mode 100644 index 0000000000000000000000000000000000000000..9e66b6af2c2521852d6cb48a6456dc8c45fb69a8 GIT binary patch literal 843 zcmV-R1GM~!P)Tlv`+2M;OO{Gjrzbx*Aj1 zprTkYCf*TmRRc<+Li1KcQKTS3qEYIjC|*E(5ycB4rFg$QD1xPyKJ>wuf+$Kv8-qSr zF({haQmaDZCYwEbX2u8cxF^{)TKvDw_nrBD|M|{0BLMat*z?dBQ^jYHQmTE^#x2zz zfH9_O^{RS=5Jf*O6)%=Mb>?^#fL9p2dhIIKTEZ~I*FFx1F$S$Q#u$VUD5VI)5Tz7K zDuR4~)*7W0HS=mBRs?qLEu`+7Iy^5{uc+g~m@$Ut=H^&F2G&}W$-L&5Uw&6YfV zq0;FJR<8ICPsh#3zAmJUg$C5SJ$?7Z%19|mrz=>u_WPoMPg%#np64N@9I&UwV!rlE zgPn5``1F|*ynOYtAMBmx&aL6=1+~%ISQ9H0frStx{6qw9sBaw5aiNmQWNEOqHu?!C zP9E*&SW3zI#toE}l|gSiE^`BH4`S%TXwHj*lgVV1Skthsw7s4-?BjZzKNKfD%VVoGLE2JY>h=ChC`bMf;N%Y+tjGwg>-UtV0BUp~{xwIxj({1@Xl! za$OxL+u0v1r6dRf04giG8J09GnD`~|FC!*`&9pOZ6euakG7dW1@aY1toJ>x0@7V}u z&MYtPSZj^7mYO;9OZOMhl|S(3Ze{ewLMW(^yhFnS;mAbMukhDu6+I_wT-MtsV2h^M61`u1FS7a#Rm@ zNTr@`-neCo0EPi$>GRTkiqcb;&hGALx!>FQcQ@dG4gm-t1?c|^?xPUs`An?-dINMd V07SVr0^a}t002ovPDHLkV1nVNiBA9k literal 0 HcmV?d00001 diff --git a/plugin/customcertificate/resources/img/64/customcertificate.png b/plugin/customcertificate/resources/img/64/customcertificate.png new file mode 100644 index 0000000000000000000000000000000000000000..02b8c9a68e0052d4f9674c3152e2c318a0cfb12b GIT binary patch literal 2871 zcmV-73&`||P)};7D3^P~!2kdX*-1n}RCt{2ntO~~RUOAa=e}m{ymxo@ zz3q0prO+-Ywbryy2nbSWP}Imn5ha8WMz4!RXbY^#U?sR9j+p^$(lTBuS=XZXubMEi{&hK1U z!V;FSgeCm{LQMbX-tG5xD~{ZVMEkG|I;9Xesx)5Swr$(!6zMp&Z@)WHbd0y8lx-i5 zb*Bf4P{#8Cd+_et?#=*HD_txamr5bqzInq<2q79vIK^0K-P7esDS7l)k0cGt*bF=k z(*htBX&}qLGnovTOa{xcD3*$-s!GTVA%vhj{icm036HU`7l}cEag+vkz1_>Ae zJkKLv$m6*lM!+B#2;zAjmSy2M4w|MB3WWeD6bh6|r7BGbfoa~=p%B?@wi;DMQHX>iWU`qW8S!|WY&MJMc~x2@5}{ZuQYw|Iv|uoZ z5CY4xeDzdTPzXUR8Y7)a&reSv$m-Rrs{xn}-(+XoHibf=X8U-aM=qDEqg9-rPS0d& zX+nr-G@74GCaYtrs`~Qy?tpM8jH;?gDeHuLp2xv= z-@$d=c^TC#<~zTutD8tTQZru&K_nbON?FJ68vtF`iAJI|$*m&rg`GRt`{o<-GVXLB z7&N)*#+$1vE`%T&jS>h1e1qCB31)NIY6kwOVY=Ny2y(d`wrw}J1EiD`tU`@|>A3i! zOBfhfS@Y^yXq-&YySsbPv?;eLvhsyFyafcc&QCW;RaFL8u9~x9A68D7ZJuucLZJ|f zqMT5`V$S}4JTntx1-=0=O_O*mPCApGlaY?&@QX(u#C z6Y&J5>Cf*Q0LO92P3GpD1a)2Kip#I0*r4c`FQ(g|YdWSGYI?4sDC8z{xUSp$YcMyN z^Q{5=44R1*{VSR>>~!V?&-2LUvLq6Tx-s7Xc&^v*EwE5Vov!&6n(MgDP0_Fz zotn@5U@+)A?z;nGu^491#Ih~F;YH(Av%F@|Bp#3ZjyLT6RT5yc$`)%-X7j;63FdOx zMf;>n;l&UXAq19fkzHM0(=HM$HlPoiEdWMGk)*kYIE|@dzLg zD;_|!UOTJKcr4z0KcLd3t(*g9Gy2iT9${>JY%c!PVcof(;};?OA;T*Xlf!85eGf`U5UtmM-sL-82*E^pg7!i?fk2>c+;;~|PEIym2yNN2mF$eR z_*~K1(dkQ<@85+y`ZK)nDrD>|ln-_zoB}d`5V!3@+_ul4j6H}Le+*^l-^{q_tFb>f znQOijuE=R-%l7v40=={H%?C353~uKZWOxTZXn?+8(jA!-%VWhPm+*B=3aSRz7LJEN# z`*}lAtu3uYA`#y)-vE?KrD{{u4?AByz@ejQT(ACoUBOek-el*i2N)gC`i|!djf*V+ znR^kOG%~gZ6bBKz7=%&F6zBvgK?{Mk-&c;JC}gr39LH(?5lCm!v(^AtY(A^{yI_|v z1A5K4=SdD7O;ap6Oje{1>h95Q-3# z9FMMKl$NN;IcIc%8bdkjdAz-w5cVjgzOAU^|A68+7)jjr3z2#&dVZKY|8fg=-rh!3 z_px=Z{QyN#%2(7MG!DS|>sGMm&BOF}CwSodU#uJ7{rbDCT{-n?`|O560EPyqXln*K zSToSkP)FIAAR=-e$_G0T_88J=!R`41O5rf_y{C|+91beG-Zc5f4}Qnv_gqKVtbI%> zPFJqoXAJ;AOG^vKjvb?575wcIf@HYFbAS6&L)#DTeeuN3%|QS1el~7AuZ}Ky?m`(U zFRlFGX_U+x$if)U?OnzG^m_g~vJy*Q#@@Hbxca+~^Xq%AC7GBy^$8&enIT$RTYdHV zR)Wn`fQD}P78dhG$mggEfZmN*et+4v;vLA*?HsT>x%CN;)~;SkMT>m;D5|Eh_rOs$ zT>n%4@RJ)@zoxg&pQd-f#KZ)aZBkl{nQD&*R%yZ)Dqz7vV|CTknpuEd$EAX?1@)tNW)``nl(KA_2oNny&;~TU!_X&G6=R1J&OfhWZ-v1#|^P z2of!c<^llB>sIJ=LkRfs?OPe>Z6y+_Kju#HmHo)1GtJilwq;kDE1$G2Mtu2_b0|5D zuj!|kzqV~Rp96G5uQKoX&mJOS|ARz%!U=}v)4)8TzU2QT^Gx;s^9VHx?a-(7DCj~{P>_;HIswhg$$6l#aDtz2`M?3x_k;!DDv1qjF)S0W#WMpK7$^0a`uG87k zNjMy4WMqVFE?X6vY)djaI!bCHg_M$(L<`;B-K0_}#*d9tDwT*tBJ}n4Vc8a=BcoWh zg&8#I>Fq%g3WrAylh5Zd41=!DuBx6)HiN3Dw6(R--rmm0=m@D)Sx+LKpr@yYR4T>T z_!y4k5Dtgy^n^koy1Tk-uEtWSR18P}6B-Zz!nS1xE2~;L4-5_t)bw(YnzrVkZ*4xv2yaP3bWHMRzDW72&#GCP~RGUd;j3zO8N9NPoinsf~5b&_7-s*M*_U}{^7%b3TPTo1WG%e z-LY?A`LZ9T)0xeR5RGf#k1#wbz0uLpT|1uLu@5K#ML{{YMu9jG0fLmHv$&S;RCwBA{Qv(y13tjQ$jHd>;>8OT zRUk1K%>rU@b8|CHnKA`Kw~~?)16b+LpFjVj>jZ(VTem_?U}a@xm^W`8!|T_t!4ipy zi46De-)CTFXNQ>ZAI+s8otH0PW-vE5hnVo^&mRUB78bYyZ*Ok~Ha0ed2|yr=%bNfJ z#7Hn4Kw$uLHL?#t0OW9%fB*h5oIZV;;rjLKC^|VgIU$UvPoMq=g#!rx`t=KpfB*gs z#_jFx|CxXQ91eYbeGI+5yz>Sxbl!UXHK>kGsmX?+XM+310 zD710-@*6+^5ex@H0GwW)J$v^5`}gk*A|fIT*wPI){^re_86G}-2#HHjSR#9D#*7)D zB!p=jFhFv1bHN6|{LTV185DdVdcuSW48Sl((O6bihT#HGLV+8=%*+fA_u}GWtbX0F zVFSahTeldjt*yZr#Q>1)gg`(*0GN)6iD8hHm1TJI<_$cWxW51a$a&~l20akbL4SWg z1I5t@5P*~_Ms*N~g71g$OGOLYU?q3}PmAkcN45#R%mB#HE35CmH%h|`T=nkGiT5r*N%-@%#PC?i_1DUM^Q z51vW5Urs+Y0Lia=UTb)=ETcTniBGJiX`%z<0E%7L?Xrsl*0yb%s;Z*8uE}v6QWQmW zFpeWzEXyJl56)}bmMLEp1*;9iAPBgCmSw%XI@fh+o@ZLKn9?+*HJ|qy{000PJ;NX6aWjO%tyRb|G4ffA|PCz8|*pLjdBdSVuu1h)#?Zk%ZFN@CSYZbU=GC zHupjPg!L5=Nl;y3L6WqBX*XzZ*sdCJkc|pIwd>1J3I4c-ed3qL(L>gRp&+H z>Jg@pQ>iZb;tSPGdQ~uatE!5N30MJ>C?o)DFsC{K95$IuOfNzQ9FND&9^mzGIP~tB z&*v<3kO1>ylmh*^SS(1Bb*j<74TJI>`X;5eObBMN}sMfaqKRw_en?EsyK{e$Oi8Gy}5rI{|16 z46!6hxGQ|$r##QebzO?0$efD|=&tGn0EcTsd9&HDEJUE-7|@94d1P6Z;hm;wm~Z1a z<};x|(SGQFix|LycG9MVFZ0kPE6b8*vl;g`cuNa|q9|y+UX$ZERM#~Jm!>H{68>ic z&>W==j?(!S3&W6tAfV-P$rsCeB=L@a*ww-U(u-**4c|@$xBP~EajfgVucW36!oA39%N8s&Kz`stX^Tl18UtiJhpJuc9Q}+Sa`5LX}rZ2g&QJMEW z&K}=g8u3eagw<-Lv>=R@>@sIPPGRPlC5q>VFeHj29fxz52Hp6`^<9ZPiE;+^cUn9Tt5|JX=c>LNv{l)~K z6OM{(=6^Do=$DEOhePF{-|suq=~REE7z`;AiD(D$cwB)njecVV0IM+pz;w7-^l=Go~E~f};KA(4r#iEjCAS4K-)(C{d zVKp$^(+GW`P*4u#$}V7`-G)m8%^Wf=kjaw_D~%#}$v<#(AG)w!nI z;c(z5sq&i)qckY44~N4i0T7inn@Xjw9ydI7`n@Rfx?&+N#>VxI(=62rB zym|lM+b2JQ7k>h;1ek>U4A|S-dtWK}@0CbQKkE~pn$o_|?f3gXZ}nK2pPij;CjvaDwxt5e@^OKM!;c%)JkHL}(sy8eh=veMZ&`RLMIdT$cjg1! zB$_KrNjWH8=mZ+%4XO&buPNOf8)*W;7}jT2t;Gh)?xhe{ev3Y>CLLhDXtcE>q+p1m zP3#Ym^2f!+MS9%;$9Ur<=OC4c(7GolC#jr*g@woq3k%-fNlj@s7JY+G)LfqC{QP`s zD%X6*EW_c`d?B-*K#KGD_&ALTkY$Qn?J2my2qMr9~08V+j30Zle@Qh?U)_xqjQ z-QDzidwaXHyu93L@@a5QQ(QAu#{nMjP%m)YKnyoGHxt`8Z9a$eU0c9#Q!g&-n}I`L^#PLgg=f#tGuwL=sk?n^Uz8IN>1cG&pnTxyC(*C2%;DpfQ^? zmo^DcVx_;Gda;o!uD@K2=uK!&6S{!}h c@Zt&Z4~)DwX6+w9-2eap07*qoM6N<$f&yWvIsgCw literal 0 HcmV?d00001 diff --git a/plugin/customcertificate/resources/js/certificate.js b/plugin/customcertificate/resources/js/certificate.js new file mode 100644 index 000000000..1fc48bdda --- /dev/null +++ b/plugin/customcertificate/resources/js/certificate.js @@ -0,0 +1,38 @@ +function dateCertificateSwitchRadioButton2(){ + $("#date_start").prop("disabled", true).val(""); + $("#date_end").prop("disabled", true).val(""); +} + +function dateCertificateSwitchRadioButton1(){ + $("#date_start").prop("disabled", false); + $("#date_end").prop("disabled", false); +} + +function dateCertificateSwitchRadioButton0(){ + $("#date_start").prop("disabled", true).val(""); + $("#date_end").prop("disabled", true).val(""); +} + +function typeDateExpedictionSwitchRadioButton(){ + $("[name=\"type_date_expediction\"]").each(function( index ) { + if ( $(this).is(":checked") && $(this).val() === "2") { + $("#day, #month, #year").prop("disabled", false); + } else { + $("#day, #month, #year").prop("disabled", true); + } + }); +} + +function contentsTypeSwitchRadioButton(){ + $("[name=\"contents_type\"]").each(function( index ) { + if ( $(this).is(":checked") && $(this).val() === "2") { + $("#contents").prop("disabled", false); + } else { + $("#contents").prop("disabled", true); + } + }); +} + +$(document).ready(function() { + $( ".datepicker" ).datepicker(); +}); diff --git a/plugin/customcertificate/src/CustomCertificatePlugin.php b/plugin/customcertificate/src/CustomCertificatePlugin.php new file mode 100644 index 000000000..92e0e7fdb --- /dev/null +++ b/plugin/customcertificate/src/CustomCertificatePlugin.php @@ -0,0 +1,332 @@ + + */ +class CustomCertificatePlugin extends Plugin +{ + public const TABLE_CUSTOMCERTIFICATE = 'plugin_customcertificate'; + public $isCoursePlugin = true; + + // When creating a new course this settings are added to the course + public $course_settings = [ + [ + 'name' => 'customcertificate_course_enable', + 'type' => 'checkbox', + ], + [ + 'name' => 'use_certificate_default', + 'type' => 'checkbox', + ], + ]; + + /** + * Constructor. + */ + protected function __construct() + { + parent::__construct( + '1.0', + 'Jose Angel Ruiz - NoSoloRed (original author), Julio Montoya', + [ + 'enable_plugin_customcertificate' => 'boolean', + ] + ); + + $this->isAdminPlugin = true; + } + + /** + * Instance the plugin. + * + * @staticvar null $result + * + * @return CustomCertificatePlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * This method creates the tables required to this plugin. + */ + public function install() + { + //Installing course settings + $this->setCourseToolDefaultVisibility(false); + $this->install_course_fields_in_all_courses(); + + $tablesToBeCompared = [self::TABLE_CUSTOMCERTIFICATE]; + $em = Database::getManager(); + $cn = $em->getConnection(); + $sm = $cn->getSchemaManager(); + $tables = $sm->tablesExist($tablesToBeCompared); + + if ($tables) { + return false; + } + + require_once api_get_path(SYS_PLUGIN_PATH).'customcertificate/database.php'; + } + + /** + * This method drops the plugin tables. + */ + public function uninstall() + { + // Deleting course settings. + $this->uninstall_course_fields_in_all_courses(); + + $tablesToBeDeleted = [self::TABLE_CUSTOMCERTIFICATE]; + foreach ($tablesToBeDeleted as $tableToBeDeleted) { + $table = Database::get_main_table($tableToBeDeleted); + $sql = "DROP TABLE IF EXISTS $table"; + Database::query($sql); + } + $this->manageTab(false); + } + + /** + * This method update the previous plugin tables. + */ + public function update() + { + $oldCertificateTable = 'gradebook_certificate_alternative'; + $base = api_get_path(WEB_UPLOAD_PATH); + if (Database::num_rows(Database::query("SHOW TABLES LIKE '$oldCertificateTable'")) == 1) { + $sql = "SELECT * FROM $oldCertificateTable"; + $res = Database::query($sql); + while ($row = Database::fetch_assoc($res)) { + $pathOrigin = $base.'certificates/'.$row['id'].'/'; + $params = [ + 'access_url_id' => api_get_current_access_url_id(), + 'c_id' => $row['c_id'], + 'session_id' => $row['session_id'], + 'content_course' => $row['content_course'], + 'contents_type' => intval($row['contents_type']), + 'contents' => $row['contents'], + 'date_change' => intval($row['date_change']), + 'date_start' => $row['date_start'], + 'date_end' => $row['date_end'], + 'place' => $row['place'], + 'type_date_expediction' => intval($row['type_date_expediction']), + 'day' => $row['day'], + 'month' => $row['month'], + 'year' => $row['year'], + 'logo_left' => $row['logo_left'], + 'logo_center' => $row['logo_center'], + 'logo_right' => $row['logo_right'], + 'seal' => $row['seal'], + 'signature1' => $row['signature1'], + 'signature2' => $row['signature2'], + 'signature3' => $row['signature3'], + 'signature4' => $row['signature4'], + 'signature_text1' => $row['signature_text1'], + 'signature_text2' => $row['signature_text2'], + 'signature_text3' => $row['signature_text3'], + 'signature_text4' => $row['signature_text4'], + 'background' => $row['background'], + 'margin_left' => intval($row['margin']), + 'margin_right' => 0, + 'certificate_default' => 0, + ]; + + $certificateId = Database::insert(self::TABLE_CUSTOMCERTIFICATE, $params); + + // Image manager + $pathDestiny = $base.'certificates/'.$certificateId.'/'; + + if (!file_exists($pathDestiny)) { + mkdir($pathDestiny, api_get_permissions_for_new_directories(), true); + } + + $imgList = [ + 'logo_left', + 'logo_center', + 'logo_right', + 'seal', + 'signature1', + 'signature2', + 'signature3', + 'signature4', + 'background', + ]; + foreach ($imgList as $value) { + if (!empty($row[$value])) { + copy( + $pathOrigin.$row[$value], + $pathDestiny.$row[$value] + ); + } + } + + if ($row['certificate_default'] == 1) { + $params['c_id'] = 0; + $params['session_id'] = 0; + $params['certificate_default'] = 1; + $certificateId = Database::insert(self::TABLE_CUSTOMCERTIFICATE, $params); + $pathOrigin = $base.'certificates/default/'; + $pathDestiny = $base.'certificates/'.$certificateId.'/'; + foreach ($imgList as $value) { + if (!empty($row[$value])) { + copy( + $pathOrigin.$row[$value], + $pathDestiny.$row[$value] + ); + } + } + } + } + + $sql = "DROP TABLE IF EXISTS $oldCertificateTable"; + Database::query($sql); + + echo get_lang('MessageUpdate'); + } + } + + /** + * Get certificate data. + * + * @param int $id The certificate + * @param int $userId + * + * @return array + */ + public static function getCertificateData($id, $userId) + { + $id = (int) $id; + $userId = (int) $userId; + + if (empty($id) || empty($userId)) { + return []; + } + + $certificateTable = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE); + $categoryTable = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY); + $sql = "SELECT cer.user_id AS user_id, cat.session_id AS session_id, cat.course_code AS course_code + FROM $certificateTable cer + INNER JOIN $categoryTable cat + ON (cer.cat_id = cat.id AND cer.user_id = $userId) + WHERE cer.id = $id"; + + $rs = Database::query($sql); + if (Database::num_rows($rs) > 0) { + $row = Database::fetch_assoc($rs); + $courseCode = $row['course_code']; + $sessionId = $row['session_id']; + $userId = $row['user_id']; + if (api_get_course_setting('customcertificate_course_enable', api_get_course_info($courseCode)) == 1) { + return [ + 'course_code' => $courseCode, + 'session_id' => $sessionId, + 'user_id' => $userId, + ]; + } + } + + return []; + } + + /** + * Check if it redirects. + * + * @param certificate $certificate + * @param int $certId + * @param int $userId + */ + public static function redirectCheck($certificate, $certId, $userId) + { + $certId = (int) $certId; + $userId = !empty($userId) ? $userId : api_get_user_id(); + + if (api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate') === 'true') { + $infoCertificate = self::getCertificateData($certId, $userId); + if (!empty($infoCertificate)) { + if ($certificate->user_id == api_get_user_id() && !empty($certificate->certificate_data)) { + $certificateId = $certificate->certificate_data['id']; + $extraFieldValue = new ExtraFieldValue('user_certificate'); + $value = $extraFieldValue->get_values_by_handler_and_field_variable( + $certificateId, + 'downloaded_at' + ); + if (empty($value)) { + $params = [ + 'item_id' => $certificate->certificate_data['id'], + 'extra_downloaded_at' => api_get_utc_datetime(), + ]; + $extraFieldValue->saveFieldValues($params); + } + } + + $url = api_get_path(WEB_PLUGIN_PATH).'customcertificate/src/print_certificate.php'. + '?student_id='.$infoCertificate['user_id']. + '&course_code='.$infoCertificate['course_code']. + '&session_id='.$infoCertificate['session_id']; + header('Location: '.$url); + exit; + } + } + } + + /** + * Get certificate info. + * + * @param int $courseId + * @param int $sessionId + * @param int $accessUrlId + * + * @return array + */ + public static function getInfoCertificate($courseId, $sessionId, $accessUrlId) + { + $courseId = (int) $courseId; + $sessionId = (int) $sessionId; + $accessUrlId = !empty($accessUrlId) ? (int) $accessUrlId : 1; + + $table = Database::get_main_table(self::TABLE_CUSTOMCERTIFICATE); + $sql = "SELECT * FROM $table + WHERE + c_id = $courseId AND + session_id = $sessionId AND + access_url_id = $accessUrlId"; + $result = Database::query($sql); + $resultArray = []; + if (Database::num_rows($result) > 0) { + $resultArray = Database::fetch_array($result); + } + + return $resultArray; + } + + /** + * Get default certificate info. + * + * @param int $accessUrlId + * + * @return array + */ + public static function getInfoCertificateDefault($accessUrlId) + { + $accessUrlId = !empty($accessUrlId) ? (int) $accessUrlId : 1; + + $table = Database::get_main_table(self::TABLE_CUSTOMCERTIFICATE); + $sql = "SELECT * FROM $table + WHERE certificate_default = 1 AND access_url_id = $accessUrlId"; + $result = Database::query($sql); + $resultArray = []; + if (Database::num_rows($result) > 0) { + $resultArray = Database::fetch_array($result); + } + + return $resultArray; + } +} diff --git a/plugin/customcertificate/src/customcertificate.ajax.php b/plugin/customcertificate/src/customcertificate.ajax.php new file mode 100644 index 000000000..b6e6e6cc8 --- /dev/null +++ b/plugin/customcertificate/src/customcertificate.ajax.php @@ -0,0 +1,51 @@ +get('enable_plugin_customcertificate') == 'true'; + +if ($enable === false) { + api_not_allowed(); +} + +$action = isset($_GET['a']) ? $_GET['a'] : null; + +switch ($action) { + case 'delete_certificate': + $table = Database::get_main_table(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE); + $courseId = isset($_POST['courseId']) ? (int) $_POST['courseId'] : 0; + $sessionId = isset($_POST['sessionId']) ? (int) $_POST['sessionId'] : 0; + $accessUrlId = isset($_POST['accessUrlId']) ? (int) $_POST['accessUrlId'] : 1; + + if (!empty($courseId)) { + $sql = "DELETE FROM $table + WHERE + c_id = $courseId AND + session_id = $sessionId AND + access_url_id = $accessUrlId"; + Database::query($sql); + echo Display::return_message( + get_plugin_lang('SuccessDelete', 'CustomCertificatePlugin'), + 'success' + ); + } else { + echo Display::return_message( + get_plugin_lang('ProblemDelete', 'CustomCertificatePlugin'), + 'error', + false + ); + } + + break; +} diff --git a/plugin/customcertificate/src/export_pdf_all_in_one.php b/plugin/customcertificate/src/export_pdf_all_in_one.php new file mode 100644 index 000000000..759cd0f38 --- /dev/null +++ b/plugin/customcertificate/src/export_pdf_all_in_one.php @@ -0,0 +1,708 @@ +get('enable_plugin_customcertificate') === 'true'; +$tblProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); +$tblCourse = Database::get_main_table(TABLE_MAIN_COURSE); +$tblSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE); +$tblSessionRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION); + +define('NO_DATE_FILTER', 0); +define('DATE_BEGIN_FILTER', 1); +define('DATE_END_FILTER', 2); +define('ALL_DATE_FILTER', 3); + +if (!$enable) { + api_not_allowed(true, $plugin->get_lang('ToolDisabled')); +} + +$currentLocalTime = api_get_local_time(); +$accessUrlId = api_get_current_access_url_id(); + +$sessionId = isset($_GET['session_id']) ? (int) $_GET['session_id'] : null; +$dateBegin = isset($_GET['date_begin']) ? strtotime($_GET['date_begin']) : null; +$dateEnd = isset($_GET['date_end']) ? strtotime($_GET['date_end'].' 23:59:59') : null; + +if (api_is_multiple_url_enabled()) { + if ($accessUrlId != -1) { + $result = Database::select( + '*', + "$tblSessionRelAccessUrl", + [ + 'where' => [ + "access_url_id = ? AND session_id = ?" => [$accessUrlId, $sessionId], + ], + ] + ); + + if (empty($result)) { + api_not_allowed(); + } + } +} + +$exportAllInOne = isset($_GET['export_pdf']) ? (int) $_GET['export_pdf'] : false; +$exportZip = isset($_GET['export_zip']) ? (int) $_GET['export_zip'] : false; + +$filterDate = 0; +if (!empty($dateBegin)) { + $filterDate += DATE_BEGIN_FILTER; +} +if (!empty($dateEnd)) { + $filterDate += DATE_END_FILTER; +} + +$filterCheckList = []; +$extraField = new ExtraField('user'); +$extraFieldsAll = $extraField->get_all(['filter = ?' => 1], 'option_order'); +foreach ($extraFieldsAll as $field) { + if (!empty($_GET['extra_'.$field['variable']])) { + $filterCheckList[$field['id']] = $field; + } +} + +$result = Database::select( + 'c.id, c.code', + "$tblCourse c INNER JOIN $tblSessionRelCourse r ON c.id = r.c_id", + [ + 'where' => [ + "r.session_id = ? " => [$sessionId], + ], + ] +); + +foreach ($result as $value) { + $courseId = $value['id']; + $courseCode = $value['code']; + + $cats = Category::load( + null, + null, + $courseCode, + null, + null, + $sessionId, + 'ORDER BY id' + ); + + if (empty($cats)) { + // first time + $cats = Category::load( + 0, + null, + $courseCode, + null, + null, + $sessionId, + 'ORDER BY id' + ); + } + + $selectCat = (int) $cats[0]->get_id(); + $certificateList = []; + $certificateListAux = GradebookUtils::get_list_users_certificates($selectCat); + + foreach ($certificateListAux as $value) { + $created_at = strtotime(api_get_local_time($value['created_at'])); + $value['category_id'] = $selectCat; + $value['c_id'] = $courseId; + $value['course_code'] = $courseCode; + switch ($filterDate) { + case NO_DATE_FILTER: + $certificateList[] = $value; + break; + case DATE_BEGIN_FILTER: + if ($created_at >= $dateBegin) { + $certificateList[] = $value; + } + break; + case DATE_END_FILTER: + if ($created_at <= $dateEnd) { + $certificateList[] = $value; + } + break; + case ALL_DATE_FILTER: + if ($created_at >= $dateBegin && $created_at <= $dateEnd) { + $certificateList[] = $value; + } + break; + } + } + + // Filter extra field + foreach ($certificateList as $key => $value) { + foreach ($filterCheckList as $fieldId => $field) { + $extraFieldValue = new ExtraFieldValue('user'); + $extraFieldValueData = $extraFieldValue->get_values_by_handler_and_field_id( + $value['user_id'], + $fieldId + ); + + if (empty($extraFieldValueData)) { + unset($certificateList[$key]); + break; + } + + switch ($field['field_type']) { + case ExtraField::FIELD_TYPE_TEXT: + case ExtraField::FIELD_TYPE_ALPHANUMERIC: + $pos = stripos($extraFieldValueData['value'], $_GET['extra_'.$field['variable']]); + if ($pos === false) { + unset($certificateList[$key]); + } + break; + case ExtraField::FIELD_TYPE_RADIO: + $valueRadio = $_GET['extra_'.$field['variable']]['extra_'.$field['variable']]; + if ($extraFieldValueData['value'] != $valueRadio) { + unset($certificateList[$key]); + } + break; + case ExtraField::FIELD_TYPE_SELECT: + if ($extraFieldValueData['value'] != $_GET['extra_'.$field['variable']]) { + unset($certificateList[$key]); + } + break; + } + } + } +} + +$userList = []; +foreach ($certificateList as $index => $value) { + $infoUser = api_get_user_info($value['user_id']); + $infoUser['category_id'] = $value['category_id']; + $infoUser['c_id'] = $value['c_id']; + $infoUser['course_code'] = $value['course_code']; + $userList[] = $infoUser; +} + +$sessionInfo = []; +if ($sessionId > 0) { + $sessionInfo = SessionManager::fetch($sessionId); +} + +$path = api_get_path(WEB_UPLOAD_PATH).'certificates/'; +$htmlList = []; + +foreach ($userList as $userInfo) { + $courseId = $userInfo['c_id']; + $courseCode = $userInfo['course_code']; + $studentId = $userInfo['user_id']; + + $courseInfo = api_get_course_info($courseCode); + $allowCustomCertificate = api_get_course_setting('customcertificate_course_enable', $courseInfo); + if (!$allowCustomCertificate) { + continue; + } + + // Get info certificate + $infoCertificate = CustomCertificatePlugin::getInfoCertificate($courseId, $sessionId, $accessUrlId); + + if (!is_array($infoCertificate)) { + $infoCertificate = []; + } + + if (empty($infoCertificate)) { + $infoCertificate = CustomCertificatePlugin::getInfoCertificateDefault($accessUrlId); + + if (empty($infoCertificate)) { + Display::display_header($plugin->get_lang('PrintCertificate')); + echo Display::return_message($plugin->get_lang('ErrorTemplateCertificate'), 'error'); + Display::display_footer(); + exit; + } + } + + $workSpace = intval(297 - $infoCertificate['margin_left'] - $infoCertificate['margin_right']); + $widthCell = intval($workSpace / 6); + + $htmlText = ''; + if (!$exportAllInOne) { + $htmlText = ''; + $htmlText .= ' + '; + $htmlText .= ' + '; + $htmlText .= ''; + } + $studentId = $userInfo['user_id']; + + if (empty($infoCertificate['background'])) { + $htmlText .= '
    '; + } else { + $urlBackground = $path.$infoCertificate['background']; + $htmlText .= '
    '; + } + + if (!empty($infoCertificate['logo_left'])) { + $logoLeft = ' + '; + } else { + $logoLeft = ''; + } + + $logoCenter = ''; + if (!empty($infoCertificate['logo_center'])) { + $logoCenter = ' + '; + } + + $logoRight = ''; + if (!empty($infoCertificate['logo_right'])) { + $logoRight = ' + '; + } + + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= '
    '; + + $allUserInfo = DocumentManager::get_all_info_to_certificate( + $studentId, + $courseCode, + $sessionId, + false + ); + + $myContentHtml = $infoCertificate['content_course']; + $myContentHtml = str_replace(chr(13).chr(10).chr(13).chr(10), chr(13).chr(10), $myContentHtml); + $infoToBeReplacedInContentHtml = $allUserInfo[0]; + $infoToReplaceInContentHtml = $allUserInfo[1]; + $myContentHtml = str_replace( + $infoToBeReplacedInContentHtml, + $infoToReplaceInContentHtml, + $myContentHtml + ); + + $startDate = ''; + $endDate = ''; + switch ($infoCertificate['date_change']) { + case 0: + if (!empty($sessionInfo['access_start_date'])) { + $startDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_start_date']))); + } + if (!empty($sessionInfo['access_end_date'])) { + $endDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_end_date']))); + } + break; + case 1: + $startDate = date("d/m/Y", strtotime($infoCertificate['date_start'])); + $endDate = date("d/m/Y", strtotime($infoCertificate['date_end'])); + break; + } + + $myContentHtml = str_replace( + '((start_date))', + $startDate, + $myContentHtml + ); + + $myContentHtml = str_replace( + '((end_date))', + $endDate, + $myContentHtml + ); + + $dateExpediction = ''; + if ($infoCertificate['type_date_expediction'] != 3) { + $dateExpediction .= $plugin->get_lang('ExpedictionIn').' '.$infoCertificate['place']; + if ($infoCertificate['type_date_expediction'] == 1) { + $dateExpediction .= $plugin->get_lang('to').api_format_date(time(), DATE_FORMAT_LONG); + } elseif ($infoCertificate['type_date_expediction'] == 2) { + $dateFormat = $plugin->get_lang('formatDownloadDate'); + if (!empty($infoCertificate['day']) && + !empty($infoCertificate['month']) && + !empty($infoCertificate['year']) + ) { + $dateExpediction .= sprintf( + $dateFormat, + $infoCertificate['day'], + $infoCertificate['month'], + $infoCertificate['year'] + ); + } else { + $dateExpediction .= sprintf( + $dateFormat, + '......', + '....................', + '............' + ); + } + } elseif ($infoCertificate['type_date_expediction'] == 4) { + $dateExpediction .= $plugin->get_lang('to').$infoToReplaceInContentHtml[9]; //date_certificate_no_time + } else { + if (!empty($sessionInfo)) { + $dateInfo = api_get_local_time($sessionInfo['access_end_date']); + $dateExpediction .= $plugin->get_lang('to').api_format_date($dateInfo, DATE_FORMAT_LONG); + } + } + } + + $myContentHtml = str_replace( + '((date_expediction))', + $dateExpediction, + $myContentHtml + ); + + $myContentHtml = strip_tags( + $myContentHtml, + '

  • '; + + $htmlText .= ''; + $htmlText .= ' + + + + '; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ' + + + + '; + $htmlText .= ''; + $htmlText .= '
    '. + ((!empty($infoCertificate['signature_text1'])) ? $infoCertificate['signature_text1'] : ''). + ''. + ((!empty($infoCertificate['signature_text2'])) ? $infoCertificate['signature_text2'] : ''). + ''. + ((!empty($infoCertificate['signature_text3'])) ? $infoCertificate['signature_text3'] : ''). + ''. + ((!empty($infoCertificate['signature_text4'])) ? $infoCertificate['signature_text4'] : ''). + ' + '.((!empty($infoCertificate['seal'])) ? $plugin->get_lang('Seal') : ''). + '
    '. + ((!empty($infoCertificate['signature1'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature2'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature3'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature4'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['seal'])) + ? '' + : ''). + '
    '; + $htmlText .= '

    '; + + // Rear certificate + if ($infoCertificate['contents_type'] != 3) { + $htmlText .= '
    '; + if ($infoCertificate['contents_type'] == 0) { + $courseDescription = new CourseDescription(); + $contentDescription = $courseDescription->get_data_by_description_type(3, $courseId, 0); + $domd = new DOMDocument(); + libxml_use_internal_errors(true); + if (isset($contentDescription['description_content'])) { + $domd->loadHTML($contentDescription['description_content']); + } + libxml_use_internal_errors(false); + $domx = new DOMXPath($domd); + $items = $domx->query("//li[@style]"); + foreach ($items as $item) { + $item->removeAttribute("style"); + } + + $items = $domx->query("//span[@style]"); + foreach ($items as $item) { + $item->removeAttribute("style"); + } + + $output = $domd->saveHTML(); + $htmlText .= getIndexFiltered($output); + } + + if ($infoCertificate['contents_type'] == 1) { + $items = []; + $categoriesTempList = learnpath::getCategories($courseId); + $categoryTest = new CLpCategory(); + $categoryTest->setId(0); + $categoryTest->setName($plugin->get_lang('WithOutCategory')); + $categoryTest->setPosition(0); + $categories = [$categoryTest]; + + if (!empty($categoriesTempList)) { + $categories = array_merge($categories, $categoriesTempList); + } + + foreach ($categories as $item) { + $categoryId = $item->getId(); + + if (!learnpath::categoryIsVisibleForStudent($item, api_get_user_entity($studentId))) { + continue; + } + + $sql = "SELECT 1 + FROM $tblProperty + WHERE tool = 'learnpath_category' + AND ref = $categoryId + AND visibility = 0 + AND (session_id = $sessionId OR session_id IS NULL)"; + $res = Database::query($sql); + if (Database::num_rows($res) > 0) { + continue; + } + + $list = new LearnpathList( + $studentId, + $courseCode, + $sessionId, + null, + false, + $categoryId + ); + + $flat_list = $list->get_flat_list(); + + if (empty($flat_list)) { + continue; + } + + if (count($categories) > 1 && count($flat_list) > 0) { + if ($item->getName() != $plugin->get_lang('WithOutCategory')) { + $items[] = '

    '.$item->getName().'

    '; + } + } + + foreach ($flat_list as $learnpath) { + $lpId = $learnpath['lp_old_id']; + $sql = "SELECT 1 + FROM $tblProperty + WHERE tool = 'learnpath' + AND ref = $lpId AND visibility = 0 + AND (session_id = $sessionId OR session_id IS NULL)"; + $res = Database::query($sql); + if (Database::num_rows($res) > 0) { + continue; + } + $lpName = $learnpath['lp_name']; + $items[] = $lpName.'
    '; + } + $items[] = '
    '; + } + + if (count($items) > 0) { + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= '
    '; + $i = 0; + foreach ($items as $value) { + if ($i == 50) { + $htmlText .= ''; + } + $htmlText .= $value; + $i++; + } + $htmlText .= '
    '; + } + $htmlText .= ''; + } + + if ($infoCertificate['contents_type'] == 2) { + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= '
    '; + $myContentHtml = strip_tags( + $infoCertificate['contents'], + '

    '; + $htmlText .= '
  • '; + } + $htmlText .= ''; + } + + if (!$exportAllInOne) { + $htmlText .= ''; + } + $fileName = 'certificate_'.$userInfo['course_code'].'_'.$userInfo['complete_name'].'_'.$currentLocalTime; + $htmlList[$fileName] = $htmlText; +} + +$fileList = []; +$archivePath = api_get_path(SYS_ARCHIVE_PATH).'certificates/'; +if (!is_dir($archivePath)) { + mkdir($archivePath, api_get_permissions_for_new_directories()); +} + +if ($exportAllInOne) { + $params = [ + 'pdf_title' => 'Certificate', + 'pdf_description' => '', + 'format' => 'A4-L', + 'orientation' => 'L', + 'left' => 15, + 'top' => 15, + 'bottom' => 0, + ]; + $pdf = new PDF($params['format'], $params['orientation'], $params); + + $contentAllCertificate = ''; + foreach ($htmlList as $fileName => $content) { + $contentAllCertificate .= $content; + } + + if (!empty($contentAllCertificate)) { + $certificateContent = ''; + $certificateContent .= ' + '; + $certificateContent .= ' + '; + $certificateContent .= ''; + $certificateContent .= $contentAllCertificate; + $certificateContent .= ''; + + $pdf->content_to_pdf( + $certificateContent, + '', + 'certificate'.date('Y_m_d_His'), + null, + 'D', + false, + null, + false, + false, + false + ); + } +} else { + foreach ($htmlList as $fileName => $content) { + $fileName = api_replace_dangerous_char($fileName); + $params = [ + 'filename' => $fileName, + 'pdf_title' => 'Certificate', + 'pdf_description' => '', + 'format' => 'A4-L', + 'orientation' => 'L', + 'left' => 15, + 'top' => 15, + 'bottom' => 0, + ]; + $pdf = new PDF($params['format'], $params['orientation'], $params); + if ($exportZip) { + $filePath = $archivePath.$fileName.'.pdf'; + $pdf->content_to_pdf($content, '', $fileName, null, 'F', true, $filePath, false, false, false); + $fileList[] = $filePath; + } else { + $pdf->content_to_pdf($content, '', $fileName, null, 'D', false, null, false, false, false); + } + } + + if (!empty($fileList)) { + $zipFile = $archivePath.'certificates_'.api_get_unique_id().'.zip'; + $zipFolder = new PclZip($zipFile); + foreach ($fileList as $file) { + $zipFolder->add($file, PCLZIP_OPT_REMOVE_ALL_PATH); + } + $name = 'certificates_'.$currentLocalTime.'.zip'; + DocumentManager::file_send_for_download($zipFile, true, $name); + exit; + } +} + +function getIndexFiltered($index) +{ + $txt = strip_tags($index, ""); + $txt = str_replace(chr(13).chr(10).chr(13).chr(10), chr(13).chr(10), $txt); + $lines = explode(chr(13).chr(10), $txt); + $text1 = ''; + for ($x = 0; $x < 47; $x++) { + if (isset($lines[$x])) { + $text1 .= $lines[$x].chr(13).chr(10); + } + } + + $text2 = ''; + for ($x = 47; $x < 94; $x++) { + if (isset($lines[$x])) { + $text2 .= $lines[$x].chr(13).chr(10); + } + } + + $showLeft = str_replace(chr(13).chr(10), "
    ", $text1); + $showRight = str_replace(chr(13).chr(10), "
    ", $text2); + $result = ''; + $result .= ''; + $result .= ''; + $result .= ''; + $result .= ''; + $result .= '
    '.$showLeft.''.$showRight.'
    '; + + return $result; +} diff --git a/plugin/customcertificate/src/index.php b/plugin/customcertificate/src/index.php new file mode 100644 index 000000000..c8cdf4f50 --- /dev/null +++ b/plugin/customcertificate/src/index.php @@ -0,0 +1,978 @@ +get_lang('CertificateSetting'); +$enable = $plugin->get('enable_plugin_customcertificate') == 'true'; +$accessUrlId = api_get_current_access_url_id(); +$course_info = api_get_course_info(); + +if ($isDefault === 1) { + $courseId = 0; + $courseCode = ''; + $sessionId = 0; + $enableCourse = false; + $useDefault = true; + $defaultCertificate = 1; + $nameTools = $plugin->get_lang('CertificateSettingDefault'); + $urlParams = '?default=1'; +} else { + $courseId = api_get_course_int_id(); + $courseCode = api_get_course_id(); + $sessionId = api_get_session_id(); + $enableCourse = api_get_course_setting('customcertificate_course_enable', $course_info) == 1 ? true : false; + $useDefault = api_get_course_setting('use_certificate_default', $course_info) == 1 ? true : false; + $defaultCertificate = 0; + $urlParams = '?'.api_get_cidreq(); +} + +if (!$enable) { + api_not_allowed(true, $plugin->get_lang('ToolDisabled')); +} + +if (!$enableCourse && !$useDefault) { + api_not_allowed(true, $plugin->get_lang('ToolDisabledCourse')); +} + +if ($enableCourse && $useDefault) { + api_not_allowed(true, $plugin->get_lang('ToolUseDefaultSettingCourse')); +} + +$allow = api_is_platform_admin() || api_is_teacher(); +if (!$allow) { + api_not_allowed(true); +} + +$table = Database::get_main_table(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE); +$htmlHeadXtra[] = api_get_js_simple( + api_get_path(WEB_PLUGIN_PATH).'customcertificate/resources/js/certificate.js' +); +$htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css'); +$htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js'); +$htmlHeadXtra[] = api_get_css( + api_get_path(WEB_PLUGIN_PATH).'customcertificate/resources/css/form.css' +); +$htmlHeadXtra[] = ''; + +// Get info certificate +$infoCertificate = CustomCertificatePlugin::getInfoCertificate($courseId, $sessionId, $accessUrlId); + +$form = new FormValidator( + 'formEdit', + 'post', + api_get_self().$urlParams, + null, + ['class' => 'form-vertical'] +); + +if ($form->validate()) { + $formValues = $form->getSubmitValues(); + if (empty($formValues['contents'])) { + $contents = ''; + } else { + $contents = $formValues['contents']; + } + $check = Security::check_token('post'); + if ($check) { + $date_start = str_replace('/', '-', $formValues['date_start']); + $date_end = str_replace('/', '-', $formValues['date_end']); + $params = [ + 'access_url_id' => api_get_current_access_url_id(), + 'c_id' => $formValues['c_id'], + 'session_id' => $formValues['session_id'], + 'content_course' => $formValues['content_course'], + 'contents_type' => (int) $formValues['contents_type'], + 'contents' => $contents, + 'date_change' => intval($formValues['date_change']), + 'date_start' => date("Y-m-d", strtotime($date_start)), + 'date_end' => date("Y-m-d", strtotime($date_end)), + 'place' => $formValues['place'], + 'type_date_expediction' => (int) $formValues['type_date_expediction'], + 'day' => $formValues['day'], + 'month' => $formValues['month'], + 'year' => $formValues['year'], + 'signature_text1' => $formValues['signature_text1'], + 'signature_text2' => $formValues['signature_text2'], + 'signature_text3' => $formValues['signature_text3'], + 'signature_text4' => $formValues['signature_text4'], + 'margin_left' => (int) $formValues['margin_left'], + 'margin_right' => (int) $formValues['margin_right'], + 'certificate_default' => 0, + ]; + + if (intval($formValues['default_certificate'] == 1)) { + $params['certificate_default'] = 1; + } + + // Insert or Update + if ($infoCertificate['id'] > 0) { + $certificateId = $infoCertificate['id']; + Database::update($table, $params, ['id = ?' => $certificateId]); + } else { + $certificateId = Database::insert($table, $params); + } + + // Image manager + $fieldList = [ + 'logo_left', + 'logo_center', + 'logo_right', + 'seal', + 'signature1', + 'signature2', + 'signature3', + 'signature4', + 'background', + ]; + + foreach ($fieldList as $field) { + $checkLogo[$field] = false; + if (!empty($formValues['remove_'.$field]) || $_FILES[$field]['size']) { + checkInstanceImage( + $certificateId, + $infoCertificate[$field], + $field + ); + } + + if ($_FILES[$field]['size']) { + $newPicture = api_upload_file( + 'certificates', + $_FILES[$field], + $certificateId, + $formValues[$field.'_crop_result'] + ); + if ($newPicture) { + $sql = "UPDATE $table + SET $field = '".$newPicture['path_to_save']."' + WHERE id = $certificateId"; + Database::query($sql); + $checkLogo[$field] = true; + } + } + } + + // Certificate Default + if (intval($formValues['use_default'] == 1)) { + $infoCertificateDefault = CustomCertificatePlugin::getInfoCertificateDefault($accessUrlId); + if (!empty($infoCertificateDefault)) { + foreach ($fieldList as $field) { + if (!empty($infoCertificateDefault[$field]) && !$checkLogo[$field]) { + $sql = "UPDATE $table + SET $field = '".$infoCertificateDefault[$field]."' + WHERE id = $certificateId"; + Database::query($sql); + } + } + } + } + + Display::addFlash(Display::return_message(get_lang('Saved'))); + + Security::clear_token(); + header('Location: '.api_get_self().$urlParams); + exit; + } +} + +if (empty($infoCertificate)) { + $infoCertificate = CustomCertificatePlugin::getInfoCertificateDefault($accessUrlId); + + if (empty($infoCertificate)) { + $infoCertificate = [ + 'type_date_expediction' => '', + 'year' => '', + 'month' => '', + 'day' => '', + 'date_change' => '', + ]; + } + $useDefault = true; +} + +// Display the header +Display::display_header($nameTools); +$actionsLeft = Display::url( + Display::return_icon('certificate.png', get_lang('Certificate'), '', ICON_SIZE_MEDIUM), + 'print_certificate.php'.$urlParams +); +if (!empty($courseId) && !$useDefault) { + $actionsLeft .= Display::url( + Display::return_icon('delete.png', $plugin->get_lang('DeleteCertificate'), '', ICON_SIZE_MEDIUM), + 'delete_certificate.php'.$urlParams, + ['id' => 'delete_certificate'] + ); +} + +echo Display::toolbarAction( + 'toolbar-document', + [$actionsLeft] +); + +if ($useDefault && $courseId > 0) { + echo Display::return_message(get_lang('InfoFromDefaultCertificate'), 'info'); +} + +// Student and course section +$form->addHtml('

    '.strtoupper(get_lang('StudentCourseInfo')).''); +$form->addHtml('
    '); +$dir = '/'; +$courseInfo = api_get_course_info(); +$isAllowedToEdit = api_is_allowed_to_edit(null, true); +$editorConfig = [ + 'ToolbarSet' => $isAllowedToEdit ? 'Documents' : 'DocumentsStudent', + 'Width' => '100%', + 'Height' => '300', + 'cols-size' => [0, 12, 0], + 'FullPage' => true, + 'InDocument' => true, + 'CreateDocumentDir' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/', + 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/', + 'BaseHref' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document'.$dir, +]; +$form->addHtmlEditor( + 'content_course', + '', + false, + true, + $editorConfig, + true +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$strInfo = '((user_firstname))
    '; +$strInfo .= '((user_lastname))
    '; +$strInfo .= '((gradebook_institution))
    '; +$strInfo .= '((gradebook_sitename))
    '; +$strInfo .= '((teacher_firstname))
    '; +$strInfo .= '((teacher_lastname))
    '; +$strInfo .= '((official_code))
    '; +$strInfo .= '((date_certificate))
    '; +$strInfo .= '((date_certificate_no_time))
    '; +$strInfo .= '((date_simple_certificate))
    '; +$strInfo .= '((course_code))
    '; +$strInfo .= '((course_title))
    '; +$strInfo .= '((gradebook_grade))
    '; +$strInfo .= '((external_style))
    '; +$strInfo .= '((start_date))
    '; +$strInfo .= '((end_date))
    '; +$strInfo .= '((date_expediction))'; + +$createCertificate = get_lang('CreateCertificateWithTags'); +$form->addElement( + 'html', + Display::return_message($createCertificate.':
    '.$strInfo, 'normal', false) +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addHtml('
    '); + +// Contents section +$form->addHtml('
    '.strtoupper(get_lang('Contents')).''); +$extra = ''; +if (empty($infoCertificate['contents_type'])) { + $infoCertificate['contents_type'] = 0; + $extra = 'disabled'; +} + +$group = []; +$element = &$form->createElement( + 'radio', + 'contents_type', + '', + get_lang('ContentsCourseDescription'), + 0, + ['id' => 'contents_type_0', 'onclick' => 'javascript: contentsTypeSwitchRadioButton();'] +); + +$group[] = $element; + +$element = &$form->createElement( + 'radio', + 'contents_type', + '', + get_lang('ContentsIndexLearnpath'), + 1, + ['id' => 'contents_type_1', 'onclick' => 'javascript: contentsTypeSwitchRadioButton();'] +); +$group[] = $element; + +$element = &$form->createElement( + 'radio', + 'contents_type', + '', + get_lang('ContentsHide'), + 3, + ['id' => 'contents_type_3', 'onclick' => 'javascript: contentsTypeSwitchRadioButton();'] +); +$group[] = $element; + +$element = &$form->createElement( + 'radio', + 'contents_type', + '', + get_lang('ContentsCustom'), + 2, + ['id' => 'contents_type_2', 'onclick' => 'javascript: contentsTypeSwitchRadioButton();'] +); +$group[] = $element; + +$form->addGroup( + $group, + 'contents_type', + get_lang('ContentsToShow'), + null, + false +); + +$form->addHtml('
    '); +$editorConfig = [ + 'ToolbarSet' => ($isAllowedToEdit ? 'Documents' : 'DocumentsStudent'), + 'Width' => '100%', + 'Height' => '200', + 'cols-size' => [2, 10, 0], + 'FullPage' => true, + 'InDocument' => true, + 'CreateDocumentDir' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/', + 'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document/', + 'BaseHref' => api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document'.$dir, + 'id' => 'contents', + $extra, +]; +$form->addHtmlEditor( + 'contents', + get_lang('Contents'), + false, + true, + $editorConfig, + true +); +$form->addHtml('
    '); + +// Dates section +$form->addHtml('
    '.strtoupper(get_lang("Dates")).''); + +$group = []; +$option1 = &$form->createElement( + 'radio', + 'date_change', + '', + get_lang('UseDateSessionAccess'), + 0, + ['id' => 'date_change_0', 'onclick' => 'javascript: dateCertificateSwitchRadioButton0();'] +); +$group[] = $option1; + +$option2 = &$form->createElement( + 'radio', + 'date_change', + '', + get_lang('None'), + 2, + ['id' => 'date_change_2', 'onclick' => 'javascript: dateCertificateSwitchRadioButton2();'] +); +$group[] = $option2; + +$option3 = &$form->createElement( + 'radio', + 'date_change', + '', + get_lang('Custom'), + 1, + ['id' => 'date_change_1', 'onclick' => 'javascript: dateCertificateSwitchRadioButton1();'] +); +$group[] = $option3; + +$form->addGroup( + $group, + 'date_change', + get_lang('CourseDeliveryDates'), + null, + false +); +$form->addHtml('
    + +
    +
    + '.get_lang('From').' + + '.get_lang('Until').' + +
    +
    +
    '); + +$form->addText( + 'place', + get_lang('ExpectionPlace'), + false, + ['id' => 'place', 'cols-size' => [2, 5, 5], 'autofocus'] +); + +$group = []; +$option = &$form->createElement( + 'radio', + 'type_date_expediction', + '', + get_lang('UseDateEndAccessSession'), + 0, + [ + 'id' => 'type_date_expediction_0', + 'onclick' => 'javascript: dateCertificateSwitchRadioButton0();', + (($sessionId == 0) ? 'disabled' : ''), + ] +); +$group[] = $option; + +$option = &$form->createElement( + 'radio', + 'type_date_expediction', + '', + get_lang('UseDateDownloadCertificate'), + 1, + [ + 'id' => 'type_date_expediction_1', + 'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();', + ] +); +$group[] = $option; + +$option = &$form->createElement( + 'radio', + 'type_date_expediction', + '', + get_lang('UseDateGenerationCertificate'), + 4, + [ + 'id' => 'type_date_expediction_4', + 'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();', + ] +); +$group[] = $option; + +$option = &$form->createElement( + 'radio', + 'type_date_expediction', + '', + get_lang('None'), + 3, + [ + 'id' => 'type_date_expediction_3', + 'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();', + ] +); +$group[] = $option; + +$option = &$form->createElement( + 'radio', + 'type_date_expediction', + '', + get_lang('UseCustomDate'), + 2, + [ + 'id' => 'type_date_expediction_2', + 'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();', + ] +); +$group[] = $option; + +$form->addGroup( + $group, + 'type_date_expediction', + get_lang('DateExpediction'), + null, + false +); + +$form->addHtml( + '
    + +
    +
    + a + + de + + de + +
    +
    +
    ' +); +$form->addHtml('
    '); + +// Signature section +$base = api_get_path(WEB_UPLOAD_PATH); +$path = $base.'certificates/'; +$form->addHtml('
    '.strtoupper(get_lang('LogosSeal')).''); +// Logo 1 +$form->addHtml('
    '); +$form->addFile( + 'logo_left', + get_lang('LogoLeft'), + [ + 'id' => 'logo_left', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['logo_left'])) { + $form->addElement('checkbox', 'remove_logo_left', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'logo_left', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +// Logo 2 +$form->addHtml('
    '); +$form->addFile( + 'logo_center', + get_lang('LogoCenter'), + [ + 'id' => 'logo_center', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['logo_center'])) { + $form->addElement('checkbox', 'remove_logo_center', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'logo_center', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +// Logo 3 +$form->addHtml('
    '); +$form->addFile( + 'logo_right', + get_lang('LogoRight'), + [ + 'id' => 'logo_right', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['logo_right'])) { + $form->addElement('checkbox', 'remove_logo_right', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$tblProperty = api_get_supported_image_extensions(false); +$form->addRule( + 'logo_right', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addFile( + 'seal', + get_lang('Seal'), + [ + 'id' => 'seal', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['seal'])) { + $form->addElement('checkbox', 'remove_seal', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'seal', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addHtml('
    '.strtoupper(get_lang('Signatures')).''); +// signature 1 +$form->addHtml('
    '); +$form->addText( + 'signature_text1', + get_lang('SignatureText1'), + false, + ['cols-size' => [2, 10, 0], 'autofocus'] +); +$form->addFile( + 'signature1', + get_lang('Signature1'), + [ + 'id' => 'signature1', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['signature1'])) { + $form->addElement('checkbox', 'remove_signature1', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'signature1', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +// signature 2 +$form->addHtml('
    '); +$form->addText( + 'signature_text2', + get_lang('SignatureText2'), + false, + ['cols-size' => [2, 10, 0], 'autofocus'] +); +$form->addFile( + 'signature2', + get_lang('Signature2'), + [ + 'id' => 'signature2', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['signature2'])) { + $form->addElement('checkbox', 'remove_signature2', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'signature2', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +// signature 3 +$form->addHtml('
    '); +$form->addText( + 'signature_text3', + get_lang('SignatureText3'), + false, + ['cols-size' => [2, 10, 0], 'autofocus'] +); +$form->addFile( + 'signature3', + get_lang('Signature3'), + [ + 'id' => 'signature3', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['signature3'])) { + $form->addElement('checkbox', 'remove_signature3', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'signature3', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +// signature 4 +$form->addHtml('
    '); +$form->addText( + 'signature_text4', + get_lang('SignatureText4'), + false, + ['cols-size' => [2, 10, 0], 'autofocus'] +); +$form->addFile( + 'signature4', + get_lang('Signature4'), + [ + 'id' => 'signature4', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_scalable' => 'true', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['signature4'])) { + $form->addElement('checkbox', 'remove_signature4', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'signature4', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +$form->addHtml('

    '); +$form->addHtml('
    '); +$form->addHtml('
    '.strtoupper(get_lang('BackgroundCertificate')).''); +// background +$form->addFile( + 'background', + get_lang('Background'), + [ + 'id' => 'background', + 'class' => 'picture-form', + 'crop_image' => true, + 'crop_ratio' => '297 / 210', + ] +); +$form->addProgress(); +if (!empty($infoCertificate['background'])) { + $form->addElement('checkbox', 'remove_background', null, get_lang('DelImage')); + $form->addElement( + 'html', + ' + +

    ' + ); +} +$allowedPictureTypes = api_get_supported_image_extensions(false); +$form->addRule( + 'background', + get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowedPictureTypes).')', + 'filetype', + $allowedPictureTypes +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addHtml('
    '.strtoupper(get_lang('OtherOptions')).''); +$marginOptions = []; +$i = 0; +while ($i < 298) { + $marginOptions[$i] = $i.' mm'; + $i++; +} +$form->addElement( + 'select', + 'margin_left', + get_lang('MarginLeft'), + $marginOptions, + ['cols-size' => [4, 8, 0]] +); +$form->addElement( + 'select', + 'margin_right', + get_lang('MarginRight'), + $marginOptions, + ['cols-size' => [4, 8, 0]] +); +$form->addHtml('
    '); +$form->addHtml('
    '); +$form->addHtml('
    '); + +$form->addButton( + 'submit', + get_lang('SaveCertificate'), + 'check', + 'primary', + null, + null, + ['cols-size' => [5, 2, 5]], + false +); + +$form->addElement('hidden', 'formSent'); +$infoCertificate['formSent'] = 1; +$form->setDefaults($infoCertificate); +$token = Security::get_token(); +$form->addElement('hidden', 'sec_token'); +$form->addElement('hidden', 'use_default'); +$form->addElement('hidden', 'default_certificate'); +$form->addElement('hidden', 'c_id'); +$form->addElement('hidden', 'session_id'); +$form->setConstants( + [ + 'sec_token' => $token, + 'use_default' => $useDefault, + 'default_certificate' => $defaultCertificate, + 'c_id' => $courseId, + 'session_id' => $sessionId, + ] +); +echo '
    '; +echo '
    '; +echo '
    '; +echo $form->returnForm(); +echo '
    '; +echo '
    '; +echo '
    '; +Display::display_footer(); + +/** + * Delete the file if there is only one instance. + * + * @param int $certificateId + * @param string $imagePath + * @param string $field + * @param string $type + */ +function checkInstanceImage($certificateId, $imagePath, $field, $type = 'certificates') +{ + $table = Database::get_main_table(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE); + $imagePath = Database::escape_string($imagePath); + $field = Database::escape_string($field); + $certificateId = (int) $certificateId; + + $sql = "SELECT * FROM $table WHERE $field = '$imagePath'"; + $res = Database::query($sql); + if (Database::num_rows($res) == 1) { + api_remove_uploaded_file($type, $imagePath); + } + + $sql = "UPDATE $table SET $field = '' WHERE id = $certificateId"; + Database::query($sql); +} diff --git a/plugin/customcertificate/src/print_certificate.php b/plugin/customcertificate/src/print_certificate.php new file mode 100644 index 000000000..75d2c42df --- /dev/null +++ b/plugin/customcertificate/src/print_certificate.php @@ -0,0 +1,598 @@ +get('enable_plugin_customcertificate') == 'true'; +$tblProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); +$categoryId = isset($_GET['cat_id']) ? (int) $_GET['cat_id'] : 0; + +if (!$enable) { + api_not_allowed(true, $plugin->get_lang('ToolDisabled')); +} + +if ($default == 1) { + $courseId = 0; + $courseCode = ''; + $sessionId = 0; + $enableCourse = false; + $useDefault = true; +} else { + $courseId = api_get_course_int_id(); + $courseCode = api_get_course_id(); + $sessionId = api_get_session_id(); + $enableCourse = api_get_course_setting('customcertificate_course_enable') == 1 ? true : false; + $useDefault = api_get_course_setting('use_certificate_default') == 1 ? true : false; +} + +if (empty($courseCode)) { + $courseCode = isset($_REQUEST['course_code']) ? Database::escape_string($_REQUEST['course_code']) : ''; + $courseInfo = api_get_course_info($courseCode); + if (!empty($courseInfo)) { + $courseId = $courseInfo['real_id']; + } +} else { + $courseInfo = api_get_course_info($courseCode); +} + +if (empty($sessionId)) { + $sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : 0; +} + +$accessUrlId = api_get_current_access_url_id(); + +$userList = []; +$exportZip = false; +$exportAllInOne = false; +if (empty($_GET['export_all']) && empty($_GET['export_all_in_one'])) { + if (!isset($_GET['student_id'])) { + $studentId = api_get_user_id(); + } else { + $studentId = intval($_GET['student_id']); + } + $userList[] = api_get_user_info($studentId); +} else { + if (!empty($_GET['export_all'])) { + $exportZip = true; + } + if (!empty($_GET['export_all_in_one'])) { + $exportAllInOne = true; + } + $certificate_list = GradebookUtils::get_list_users_certificates($categoryId); + foreach ($certificate_list as $index => $value) { + $userList[] = api_get_user_info($value['user_id']); + } +} + +$sessionInfo = []; +if ($sessionId > 0) { + $sessionInfo = SessionManager::fetch($sessionId); +} + +$table = Database::get_main_table(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE); +$useDefault = false; +$path = api_get_path(WEB_UPLOAD_PATH).'certificates/'; + +// Get info certificate +$infoCertificate = CustomCertificatePlugin::getInfoCertificate($courseId, $sessionId, $accessUrlId); + +if (!is_array($infoCertificate)) { + $infoCertificate = []; +} + +if (empty($infoCertificate)) { + $infoCertificate = CustomCertificatePlugin::getInfoCertificateDefault($accessUrlId); + + if (empty($infoCertificate)) { + Display::display_header($plugin->get_lang('PrintCertificate')); + echo Display::return_message($plugin->get_lang('ErrorTemplateCertificate'), 'error'); + Display::display_footer(); + exit; + } else { + $useDefault = true; + } +} + +$workSpace = intval(297 - $infoCertificate['margin_left'] - $infoCertificate['margin_right']); +$widthCell = intval($workSpace / 6); +$htmlList = []; + +$currentLocalTime = api_get_local_time(); + +foreach ($userList as $userInfo) { + $htmlText = ''; + if (!$exportAllInOne) { + $htmlText = ''; + $htmlText .= ' + '; + $htmlText .= ' + '; + $htmlText .= ''; + } + $studentId = $userInfo['user_id']; + + if (empty($infoCertificate['background'])) { + $htmlText .= '
    '; + } else { + $urlBackground = $path.$infoCertificate['background']; + $htmlText .= '
    '; + } + + if (!empty($infoCertificate['logo_left'])) { + $logoLeft = ' + '; + } else { + $logoLeft = ''; + } + + $logoCenter = ''; + if (!empty($infoCertificate['logo_center'])) { + $logoCenter = ' + '; + } + + $logoRight = ''; + if (!empty($infoCertificate['logo_right'])) { + $logoRight = ' + '; + } + + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= '
    '; + + $myContentHtml = $infoCertificate['content_course']; + $myContentHtml = str_replace(chr(13).chr(10).chr(13).chr(10), chr(13).chr(10), $myContentHtml); + + if (!empty($courseCode)) { + $allUserInfo = DocumentManager::get_all_info_to_certificate( + $studentId, + $courseCode, + $sessionId, + false + ); + + $infoToBeReplacedInContentHtml = $allUserInfo[0]; + $infoToReplaceInContentHtml = $allUserInfo[1]; + $myContentHtml = str_replace( + $infoToBeReplacedInContentHtml, + $infoToReplaceInContentHtml, + $myContentHtml + ); + } + + $startDate = ''; + $endDate = ''; + switch ($infoCertificate['date_change']) { + case 0: + if (!empty($sessionInfo['access_start_date'])) { + $startDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_start_date']))); + } + if (!empty($sessionInfo['access_end_date'])) { + $endDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_end_date']))); + } + break; + case 1: + $startDate = date("d/m/Y", strtotime($infoCertificate['date_start'])); + $endDate = date("d/m/Y", strtotime($infoCertificate['date_end'])); + break; + } + + $myContentHtml = str_replace( + '((start_date))', + $startDate, + $myContentHtml + ); + + $myContentHtml = str_replace( + '((end_date))', + $endDate, + $myContentHtml + ); + + $dateExpediction = ''; + if ($infoCertificate['type_date_expediction'] != 3) { + $dateExpediction .= $plugin->get_lang('ExpedictionIn').' '.$infoCertificate['place']; + if ($infoCertificate['type_date_expediction'] == 1) { + $dateExpediction .= $plugin->get_lang('to').api_format_date(time(), DATE_FORMAT_LONG); + } elseif ($infoCertificate['type_date_expediction'] == 2) { + $dateFormat = $plugin->get_lang('formatDownloadDate'); + if (!empty($infoCertificate['day']) && + !empty($infoCertificate['month']) && + !empty($infoCertificate['year']) + ) { + $dateExpediction .= sprintf( + $dateFormat, + $infoCertificate['day'], + $infoCertificate['month'], + $infoCertificate['year'] + ); + } else { + $dateExpediction .= sprintf( + $dateFormat, + '......', + '....................', + '............' + ); + } + } elseif ($infoCertificate['type_date_expediction'] == 4) { + $dateExpediction .= $plugin->get_lang('to').$infoToReplaceInContentHtml[9]; //date_certificate_no_time + } else { + if (!empty($sessionInfo)) { + $dateInfo = api_get_local_time($sessionInfo['access_end_date']); + $dateExpediction .= $plugin->get_lang('to').api_format_date($dateInfo, DATE_FORMAT_LONG); + } + } + } + + $myContentHtml = str_replace( + '((date_expediction))', + $dateExpediction, + $myContentHtml + ); + + $myContentHtml = strip_tags( + $myContentHtml, + '

  • '; + + $htmlText .= ''; + $htmlText .= ' + + + + '; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ' + + + + '; + $htmlText .= ''; + $htmlText .= '
    '. + ((!empty($infoCertificate['signature_text1'])) ? $infoCertificate['signature_text1'] : ''). + ''. + ((!empty($infoCertificate['signature_text2'])) ? $infoCertificate['signature_text2'] : ''). + ''. + ((!empty($infoCertificate['signature_text3'])) ? $infoCertificate['signature_text3'] : ''). + ''. + ((!empty($infoCertificate['signature_text4'])) ? $infoCertificate['signature_text4'] : ''). + ' + '.((!empty($infoCertificate['seal'])) ? $plugin->get_lang('Seal') : ''). + '
    '. + ((!empty($infoCertificate['signature1'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature2'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature3'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['signature4'])) + ? '' + : ''). + ''. + ((!empty($infoCertificate['seal'])) + ? '' + : ''). + '
    '; + $htmlText .= '

    '; + + // Rear certificate + if ($infoCertificate['contents_type'] != 3) { + $htmlText .= '
    '; + if ($infoCertificate['contents_type'] == 0) { + $courseDescription = new CourseDescription(); + $contentDescription = $courseDescription->get_data_by_description_type(3, $courseId, 0); + $domd = new DOMDocument(); + libxml_use_internal_errors(true); + if (isset($contentDescription['description_content'])) { + $domd->loadHTML($contentDescription['description_content']); + } + libxml_use_internal_errors(false); + $domx = new DOMXPath($domd); + $items = $domx->query("//li[@style]"); + foreach ($items as $item) { + $item->removeAttribute("style"); + } + + $items = $domx->query("//span[@style]"); + foreach ($items as $item) { + $item->removeAttribute("style"); + } + + $output = $domd->saveHTML(); + $htmlText .= getIndexFiltered($output); + } + + if ($infoCertificate['contents_type'] == 1) { + $items = []; + $categoriesTempList = learnpath::getCategories($courseId); + $categoryTest = new CLpCategory(); + $categoryTest->setId(0); + $categoryTest->setName($plugin->get_lang('WithOutCategory')); + $categoryTest->setPosition(0); + $categories = [$categoryTest]; + + if (!empty($categoriesTempList)) { + $categories = array_merge($categories, $categoriesTempList); + } + + foreach ($categories as $item) { + $categoryId = $item->getId(); + + if (!learnpath::categoryIsVisibleForStudent($item, api_get_user_entity($studentId))) { + continue; + } + + $sql = "SELECT 1 + FROM $tblProperty + WHERE tool = 'learnpath_category' + AND ref = $categoryId + AND visibility = 0 + AND (session_id = $sessionId OR session_id IS NULL)"; + $res = Database::query($sql); + if (Database::num_rows($res) > 0) { + continue; + } + + $list = new LearnpathList( + $studentId, + $courseInfo, + $sessionId, + null, + false, + $categoryId + ); + + $flat_list = $list->get_flat_list(); + + if (empty($flat_list)) { + continue; + } + + if (count($categories) > 1 && count($flat_list) > 0) { + if ($item->getName() != $plugin->get_lang('WithOutCategory')) { + $items[] = '

    '.$item->getName().'

    '; + } + } + + foreach ($flat_list as $learnpath) { + $lpId = $learnpath['lp_old_id']; + $sql = "SELECT 1 + FROM $tblProperty + WHERE tool = 'learnpath' + AND ref = $lpId AND visibility = 0 + AND (session_id = $sessionId OR session_id IS NULL)"; + $res = Database::query($sql); + if (Database::num_rows($res) > 0) { + continue; + } + $lpName = $learnpath['lp_name']; + $items[] = $lpName.'
    '; + } + $items[] = '
    '; + } + + if (count($items) > 0) { + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= '
    '; + $i = 0; + foreach ($items as $value) { + if ($i == 50) { + $htmlText .= ''; + } + $htmlText .= $value; + $i++; + } + $htmlText .= '
    '; + } + $htmlText .= '
    '; + } + + if ($infoCertificate['contents_type'] == 2) { + $htmlText .= ''; + $htmlText .= ''; + $htmlText .= ' + + {% if not is_child %} + + {% endif %} + + + +{% endmacro %} + +{{ 'ImsLtiDescription'|get_plugin_lang('ImsLtiPlugin') }} + +{% autoescape 'html' %} + +
    +
    '; + $myContentHtml = strip_tags( + $infoCertificate['contents'], + '

    '; + $htmlText .= '
  • '; + } + $htmlText .= ''; + } + + if (!$exportAllInOne) { + $htmlText .= ''; + } + $fileName = 'certificate_'.$courseInfo['code'].'_'.$userInfo['complete_name'].'_'.$currentLocalTime; + $htmlList[$fileName] = $htmlText; +} + +$fileList = []; +$archivePath = api_get_path(SYS_ARCHIVE_PATH).'certificates/'; +if (!is_dir($archivePath)) { + mkdir($archivePath, api_get_permissions_for_new_directories()); +} + +if ($exportAllInOne) { + $params = [ + 'pdf_title' => 'Certificate', + 'pdf_description' => '', + 'format' => 'A4-L', + 'orientation' => 'L', + 'left' => 15, + 'top' => 15, + 'bottom' => 0, + ]; + $pdf = new PDF($params['format'], $params['orientation'], $params); + + $contentAllCertificate = ''; + foreach ($htmlList as $fileName => $content) { + $contentAllCertificate .= $content; + } + + if (!empty($contentAllCertificate)) { + $certificateContent = ''; + $certificateContent .= ' + '; + $certificateContent .= ' + '; + $certificateContent .= ''; + $certificateContent .= $contentAllCertificate; + $certificateContent .= ''; + + $pdf->content_to_pdf( + $certificateContent, + '', + 'certificate'.date("Y_m_d_His"), + null, + 'D', + false, + null, + false, + false, + false + ); + } +} else { + foreach ($htmlList as $fileName => $content) { + $fileName = api_replace_dangerous_char($fileName); + $params = [ + 'filename' => $fileName, + 'pdf_title' => 'Certificate', + 'pdf_description' => '', + 'format' => 'A4-L', + 'orientation' => 'L', + 'left' => 15, + 'top' => 15, + 'bottom' => 0, + ]; + $pdf = new PDF($params['format'], $params['orientation'], $params); + if ($exportZip) { + $filePath = $archivePath.$fileName.'.pdf'; + $pdf->content_to_pdf($content, '', $fileName, null, 'F', true, $filePath, false, false, false); + $fileList[] = $filePath; + } else { + $pdf->content_to_pdf($content, '', $fileName, null, 'D', false, null, false, false, false); + } + } + + if (!empty($fileList)) { + $zipFile = $archivePath.'certificates_'.api_get_unique_id().'.zip'; + $zipFolder = new PclZip($zipFile); + foreach ($fileList as $file) { + $zipFolder->add($file, PCLZIP_OPT_REMOVE_ALL_PATH); + } + $name = 'certificates_'.$courseInfo['code'].'_'.$currentLocalTime.'.zip'; + DocumentManager::file_send_for_download($zipFile, true, $name); + exit; + } +} + +function getIndexFiltered($index) +{ + $txt = strip_tags($index, ""); + $txt = str_replace(chr(13).chr(10).chr(13).chr(10), chr(13).chr(10), $txt); + $lines = explode(chr(13).chr(10), $txt); + $text1 = ''; + for ($x = 0; $x < 47; $x++) { + if (isset($lines[$x])) { + $text1 .= $lines[$x].chr(13).chr(10); + } + } + + $text2 = ''; + for ($x = 47; $x < 94; $x++) { + if (isset($lines[$x])) { + $text2 .= $lines[$x].chr(13).chr(10); + } + } + + $showLeft = str_replace(chr(13).chr(10), "
    ", $text1); + $showRight = str_replace(chr(13).chr(10), "
    ", $text2); + $result = ''; + $result .= ''; + $result .= ''; + $result .= ''; + $result .= ''; + $result .= '
    '.$showLeft.''.$showRight.'
    '; + + return $result; +} diff --git a/plugin/customcertificate/start.php b/plugin/customcertificate/start.php new file mode 100644 index 000000000..3cd81e99c --- /dev/null +++ b/plugin/customcertificate/start.php @@ -0,0 +1,34 @@ +get('enable_plugin_customcertificate') == 'true'; + +if ($enable) { + if (api_is_platform_admin() || api_is_teacher()) { + $url = 'src/index.php?'; + $url .= (isset($_GET['cidReq']) ? api_get_cidreq() : 'default=1'); + header('Location: '.$url); + exit; + } else { + $session = api_get_session_entity(api_get_session_id()); + $_course = api_get_course_info(); + $webCoursePath = api_get_path(WEB_COURSE_PATH); + $url = $webCoursePath.$_course['path'].'/index.php'.($session ? '?id_session='.$session->getId() : ''); + + Display::addFlash( + Display::return_message($plugin->get_lang('OnlyAdminPlatform')) + ); + + header('Location: '.$url); + exit; + } +} else { + api_not_allowed(true, $plugin->get_lang('ToolDisabled')); +} diff --git a/plugin/customcertificate/uninstall.php b/plugin/customcertificate/uninstall.php new file mode 100644 index 000000000..e75e6e0a9 --- /dev/null +++ b/plugin/customcertificate/uninstall.php @@ -0,0 +1,12 @@ +uninstall(); diff --git a/plugin/customcertificate/update.php b/plugin/customcertificate/update.php new file mode 100644 index 000000000..510a60e1b --- /dev/null +++ b/plugin/customcertificate/update.php @@ -0,0 +1,14 @@ +update(); diff --git a/plugin/customfooter/index.php b/plugin/customfooter/index.php new file mode 100644 index 000000000..ca91de53d --- /dev/null +++ b/plugin/customfooter/index.php @@ -0,0 +1,14 @@ + + */ +class CustomFooterPlugin extends Plugin +{ + protected function __construct() + { + parent::__construct('1.1', 'Valery Fremaux'); + } + + /** + * @return CustomFooterPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + public function get_name() + { + return 'customfooter'; + } + + public function pix_url($pixname, $size = 16) + { + global $_configuration; + + if (file_exists( + $_configuration['root_sys'].'/plugin/customplugin/pix/'.$pixname.'.png' + )) { + return $_configuration['root_web'].'/plugin/customplugin/pix/'.$pixname.'.png'; + } + if (file_exists( + $_configuration['root_sys'].'/plugin/customplugin/pix/'.$pixname.'.jpg' + )) { + return $_configuration['root_web'].'/plugin/customplugin/pix/'.$pixname.'.jpg'; + } + if (file_exists( + $_configuration['root_sys'].'/plugin/customplugin/pix/'.$pixname.'.gif' + )) { + return $_configuration['root_web'].'/plugin/customplugin/pix/'.$pixname.'.gif'; + } + + return $_configuration['root_web'].'/main/img/icons/'.$size.'/'.$pixname.'.png'; + } +} diff --git a/plugin/customfooter/plugin.php b/plugin/customfooter/plugin.php new file mode 100644 index 000000000..28bed27af --- /dev/null +++ b/plugin/customfooter/plugin.php @@ -0,0 +1,56 @@ + + */ + +/** + * Plugin details (must be present). + */ + +/* Plugin config */ +$plugin_info = CustomFooterPlugin::create()->get_info(); + +//the plugin title +$plugin_info['title'] = 'Custom Footer'; +//the comments that go with the plugin +$plugin_info['comment'] = "Drives configuration parameters that plugs custom footer notes"; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Valery Fremaux, Julio Montoya'; + +/* Plugin optional settings */ + +/* + * This form will be showed in the plugin settings once the plugin was installed + * in the plugin/hello_world/index.php you can have access to the value: $plugin_info['settings']['hello_world_show_type'] +*/ + +$form = new FormValidator('customfooter_form'); + +$plugininstance = CustomFooterPlugin::create(); + +$config = api_get_settings_params( + ['subkey = ? ' => 'customfooter', ' AND category = ? ' => 'Plugins'] +); +$form_settings = []; + +foreach ($config as $fooid => $configrecord) { + $canonic = preg_replace('/^customfooter_/', '', $configrecord['variable']); + if (in_array($canonic, ['footer_left', 'footer_right'])) { + $form_settings[$canonic] = $configrecord['selected_value']; + } +} + +// A simple select. +$form->addElement('text', 'footer_left', $plugininstance->get_lang('footerleft')); +$form->addElement('text', 'footer_right', $plugininstance->get_lang('footerright')); +$form->addButtonSave($plugininstance->get_lang('Save')); +$form->setDefaults($form_settings); + +$plugin_info['settings_form'] = $form; diff --git a/plugin/dashboard/block_course/block_course.class.php b/plugin/dashboard/block_course/block_course.class.php new file mode 100644 index 000000000..ad998b43d --- /dev/null +++ b/plugin/dashboard/block_course/block_course.class.php @@ -0,0 +1,209 @@ +user_id = $user_id; + $this->path = 'block_course'; + if ($this->is_block_visible_for_user($user_id)) { + $this->courses = CourseManager::get_courses_followed_by_drh($user_id); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array( + $user_status, + $this->permission + ) + ) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information + * about courses and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 2; + $data = []; + $content = $this->get_content_html(); + $html = ' +

    +
    '.get_lang('CoursesInformation').' + +
    +
    + '.$content.' +
    +
    + '; + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_content_html() + { + $course_data = $this->get_course_information_data(); + $content = '

    '.get_lang('YourCourseList').'

    '; + $data_table = null; + if (!empty($course_data)) { + $data_table .= ''; + $data_table .= ' + + + + + '; + $i = 1; + foreach ($course_data as $course) { + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $data_table .= ''; + if (!isset($course[2])) { + $course[2] = '0:00:00'; + } + foreach ($course as $cell) { + $data_table .= ''; + } + $data_table .= ''; + $i++; + } + $data_table .= '
    '.get_lang('CourseTitle').''.get_lang('NbStudents').''.get_lang('AvgTimeSpentInTheCourse').''.get_lang('ThematicAdvance').'
    '.$cell.'
    '; + } else { + $data_table .= get_lang('ThereIsNoInformationAboutYourCourses'); + } + $content .= $data_table; + if (!empty($course_data)) { + $content .= ''; + } + //$content .= ''; + + return $content; + } + + /** + * Get number of courses. + * + * @return int + */ + public function get_number_of_courses() + { + return count($this->courses); + } + + /** + * Get course information data. + * + * @return array + */ + public function get_course_information_data() + { + $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); + $course_data = []; + $courses = $this->courses; + $thematic = new Thematic(); + + foreach ($courses as $row_course) { + $course_code = $row_course['code']; + $courseInfo = api_get_course_info($course_code); + $courseId = $courseInfo['real_id']; + $nb_students_in_course = $avg_progress_in_course = $avg_score_in_course = $avg_time_spent_in_course = $avg_score_in_exercise = 0; + + // students directly subscribed to the course + $sql = "SELECT user_id FROM $tbl_course_user as course_rel_user + WHERE course_rel_user.status=".STUDENT." AND course_rel_user.c_id='$courseId'"; + $rs = Database::query($sql); + $users = []; + while ($row = Database::fetch_array($rs)) { + $users[] = $row['user_id']; + } + if (count($users) > 0) { + $nb_students_in_course = count($users); + $avg_time_spent_in_course = api_time_to_hms( + Tracking::get_time_spent_on_the_course($users, $courseId) / $nb_students_in_course + ); + } else { + $avg_time_spent_in_course = null; + } + $tematic_advance = $thematic->get_total_average_of_thematic_advances( + $course_code, + 0 + ); + + if (!empty($tematic_advance)) { + $tematic_advance_progress = ''.$tematic_advance.'%'; + } else { + $tematic_advance_progress = '0%'; + } + + $table_row = []; + $table_row[] = $row_course['title']; + $table_row[] = $nb_students_in_course; + $table_row[] = $avg_time_spent_in_course; + $table_row[] = $tematic_advance_progress; + $course_data[] = $table_row; + } + + return $course_data; + } +} diff --git a/plugin/dashboard/block_course/block_course.info b/plugin/dashboard/block_course/block_course.info new file mode 100644 index 000000000..c4c68a515 --- /dev/null +++ b/plugin/dashboard/block_course/block_course.info @@ -0,0 +1,6 @@ +name = "Course Block" +controller = "BlockCourse" +description = "Display information about courses inside platform" +package = Dashboard +version = 1.0 +author = Christian Fasanando diff --git a/plugin/dashboard/block_course/index.html b/plugin/dashboard/block_course/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_course/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_daily/block_daily.class.php b/plugin/dashboard/block_daily/block_daily.class.php new file mode 100644 index 000000000..243ccc520 --- /dev/null +++ b/plugin/dashboard/block_daily/block_daily.class.php @@ -0,0 +1,269 @@ +user_id = $user_id; + $this->path = 'block_daily'; + if ($this->is_block_visible_for_user($user_id)) { + $this->courses = CourseManager::get_courses_followed_by_drh( + $user_id + ); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array( + $user_status, + $this->permission + ) + ) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information about courses and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 2; + $data = []; + $content = $this->get_content_html(); + $html = '
    +
    '.get_lang('GradebookAndAttendances').' + +
    +
    + '.$content.' +
    +
    + '; + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_content_html() + { + $course_data = $this->get_course_information_data(); + $content = '

    '.get_lang('YourCourseList').'

    '; + $data_table = null; + if (!empty($course_data)) { + $data_table .= ''; + $data_table .= ' + + + + + '; + $i = 1; + foreach ($course_data as $course) { + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $data_table .= ''; + if (!isset($course[3])) { + $course[3] = get_lang('NotAvailable'); + } + foreach ($course as $cell) { + $data_table .= ''; + } + $data_table .= ''; + $i++; + } + $data_table .= '
    '.get_lang('CourseTitle').''.get_lang('NbStudents').''.get_lang('Evaluation').''.get_lang('ToolAttendance').'
    '.$cell.'
    '; + } else { + $data_table .= get_lang('ThereIsNoInformationAboutYourCourses'); + } + $content .= $data_table; + if (!empty($course_data)) { + $content .= ''; + } + //$content .= ''; + return $content; + } + + /** + * Get number of courses. + * + * @return int + */ + public function get_number_of_courses() + { + return count($this->courses); + } + + /** + * Get course information data. + * + * @return array + */ + public function get_course_information_data() + { + $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); + $course_data = []; + $courses = $this->courses; + + foreach ($courses as $row_course) { + $score = null; + $courseId = $row_course['c_id']; + $course_info = api_get_course_info_by_id($courseId); + $course_code = $course_info['code']; + if (empty($course_info)) { + continue; + } + + // Attendance table + $table_course = Database::get_course_table(TABLE_ATTENDANCE); + + $sql = "SELECT id, name, attendance_qualify_max FROM $table_course + WHERE c_id = ".$course_info['real_id']." AND active = 1 AND session_id = 0"; + $rs = Database::query($sql); + $attendance = []; + $attendances = []; + + while ($row = Database::fetch_array($rs, 'ASSOC')) { + $attendance['done'] = $row['attendance_qualify_max']; + $attendance['id'] = $row['id']; + //$attendance['name'] = $row['name']; + $attendance['course_code'] = $course_info['code']; + + if ($attendance['done'] != '0') { + $attendances[] = ''.Display::return_icon('printmgr.gif', get_lang('Print')).''; + } else { + $attendances[] = get_lang("NotAvailable"); + } + } + if (count($attendances) == 0) { + $attendances[] = get_lang("NotAvailable"); + } + + // Number of students + + $sql = "SELECT user_id FROM $tbl_course_user as course_rel_user + WHERE course_rel_user.status=".STUDENT." AND course_rel_user.c_id=$courseId"; + $rs = Database::query($sql); + $users = []; + while ($row = Database::fetch_array($rs)) { + $users[] = $row['user_id']; + } + if (count($users) > 0) { + $nb_students_in_course = count($users); + } + + if (!empty($tematic_advance)) { + $tematic_advance_progress = ''.$tematic_advance.'%'; + } else { + $tematic_advance_progress = '0%'; + } + + // Score + $tbl_grade_categories = Database::get_main_table( + TABLE_MAIN_GRADEBOOK_CATEGORY + ); + $sql = "SELECT id from ".$tbl_grade_categories." + WHERE course_code ='".$course_code."'"; + $rs = Database::query($sql); + $category = null; + while ($row = Database::fetch_array($rs)) { + $category = $row['id']; + } + + if (!empty($category)) { + $cat = Category::load($category); + $eval = $cat[0]->get_evaluations(); + if (count($eval) > 0) { + $i = 0; + foreach ($eval as $item) { + $score .= ''.$item->get_name().''; + if (count($eval) - 1 != $i) { + $score .= ', '; + } + $i++; + } + } else { + $score = get_lang("NotAvailable"); + } + } else { + $score = get_lang("NotAvailable"); + } + + $table_row = []; + $table_row[] = $row_course['title']; + $table_row[] = $nb_students_in_course; + $table_row[] = $score; + $table_row[] = $attendances[0]; + $course_data[] = $table_row; + } + + return $course_data; + } +} diff --git a/plugin/dashboard/block_daily/block_daily.info b/plugin/dashboard/block_daily/block_daily.info new file mode 100644 index 000000000..f1a604589 --- /dev/null +++ b/plugin/dashboard/block_daily/block_daily.info @@ -0,0 +1,6 @@ +name = "Gradebook & Attendances" +controller = "BlockDaily" +description = "Access to attendances in a Gradebook" +package = Dashboard +version = 1.0 +author = Marco Antonio Firmino de Sousa diff --git a/plugin/dashboard/block_daily/index.html b/plugin/dashboard/block_daily/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_daily/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.class.php b/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.class.php new file mode 100644 index 000000000..d5fb8c17e --- /dev/null +++ b/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.class.php @@ -0,0 +1,489 @@ +path = 'block_evaluation_graph'; + $this->user_id = $user_id; + $this->bg_width = 450; + $this->bg_height = 350; + if ($this->is_block_visible_for_user($user_id)) { + if (!api_is_session_admin()) { + $this->courses = CourseManager::get_courses_followed_by_drh($user_id); + } + $this->sessions = SessionManager::get_sessions_followed_by_drh($user_id); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing + * information about sessions and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 1; + $data = []; + $evaluations_base_courses_graph = $this->get_evaluations_base_courses_graph(); + $evaluations_courses_in_sessions_graph = $this->get_evaluations_courses_in_sessions_graph(); + + $html = '
    +
    + '.get_lang('EvaluationsGraph').' + +
    +
    '; + if (empty($evaluations_base_courses_graph) && empty($evaluations_courses_in_sessions_graph)) { + $html .= '

    '.api_convert_encoding(get_lang('GraphicNotAvailable'), 'UTF-8').'

    '; + } else { + // display evaluations base courses graph + if (!empty($evaluations_base_courses_graph)) { + foreach ($evaluations_base_courses_graph as $course_code => $img_html) { + $html .= '
    '.$course_code.'
    '; + $html .= $img_html; + } + } + // display evaluations base courses graph + if (!empty($evaluations_courses_in_sessions_graph)) { + foreach ($evaluations_courses_in_sessions_graph as $session_id => $courses) { + $session_name = api_get_session_name($session_id); + $html .= '
    '.$session_name.':'.get_lang('Evaluations').'
    '; + foreach ($courses as $course_code => $img_html) { + $html .= '
    '.$course_code.'
    '; + $html .= $img_html; + } + } + } + } + $html .= '
    +
    '; + + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a graph containing informations about evaluations + * inside base courses, it's used inside get_block method for showing + * it inside dashboard interface. + * + * @return string img html + */ + public function get_evaluations_base_courses_graph() + { + $graphs = []; + if (!empty($this->courses)) { + $courses_code = array_keys($this->courses); + foreach ($courses_code as $course_code) { + $cats = Category::load( + null, + null, + $course_code, + null, + null, + null, + false + ); + + if (isset($cats) && isset($cats[0])) { + $alleval = $cats[0]->get_evaluations(null, true, $course_code); + $alllinks = $cats[0]->get_links(null, true); + $users = GradebookUtils::get_all_users($alleval, $alllinks); + $datagen = new FlatViewDataGenerator($users, $alleval, $alllinks); + $evaluation_sumary = $datagen->getEvaluationSummaryResults(); + if (!empty($evaluation_sumary)) { + $items = array_keys($evaluation_sumary); + $max = $min = $avg = []; + foreach ($evaluation_sumary as $evaluation) { + $max[] = $evaluation['max']; + $min[] = !empty($evaluation['min']) ? $evaluation['min'] : 0; + $avg[] = $evaluation['avg']; + } + // Dataset definition + $dataSet = new pData(); + $dataSet->addPoints($min, 'Serie3'); + $dataSet->addPoints($avg, 'Serie2'); + $dataSet->addPoints($max, 'Serie1'); + $dataSet->addPoints($items, 'Labels'); + + $dataSet->setSerieDescription('Serie1', get_lang('Max')); + $dataSet->setSerieDescription('Serie2', get_lang('Avg')); + $dataSet->setSerieDescription('Serie3', get_lang('Min')); + $dataSet->setAbscissa('Labels'); + + $dataSet->setAbscissaName(get_lang('EvaluationName')); + + $dataSet->normalize(100, '%'); + + $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true); + + // Cache definition + $cachePath = api_get_path(SYS_ARCHIVE_PATH); + $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]); + $chartHash = $myCache->getHash($dataSet); + if ($myCache->isInCache($chartHash)) { + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } else { + /* Create the pChart object */ + $widthSize = $this->bg_width; + $heightSize = $this->bg_height; + $fontSize = 8; + $angle = 50; + + $myPicture = new pImage($widthSize, $heightSize, $dataSet); + + /* Turn of Antialiasing */ + $myPicture->Antialias = false; + + /* Add a border to the picture */ + $myPicture->drawRectangle( + 0, + 0, + $widthSize - 1, + $heightSize - 1, + [ + 'R' => 0, + 'G' => 0, + 'B' => 0, + ] + ); + + /* Set the default font */ + $myPicture->setFontProperties( + [ + 'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf', + 'FontSize' => 10, + ] + ); + + /* Do NOT Write the chart title */ + + /* Define the chart area */ + $myPicture->setGraphArea( + 50, + 30, + $widthSize - 20, + $heightSize - 100 + ); + + /* Draw the scale */ + $scaleSettings = [ + 'GridR' => 200, + 'GridG' => 200, + 'GridB' => 200, + 'DrawSubTicks' => true, + 'CycleBackground' => true, + 'Mode' => SCALE_MODE_MANUAL, + 'ManualScale' => [ + '0' => [ + 'Min' => 0, + 'Max' => 100, + ], + ], + 'LabelRotation' => $angle, + ]; + $myPicture->drawScale($scaleSettings); + + /* Turn on shadow computing */ + $myPicture->setShadow( + true, + [ + 'X' => 1, + 'Y' => 1, + 'R' => 0, + 'G' => 0, + 'B' => 0, + 'Alpha' => 10, + ] + ); + + /* Draw the chart */ + $myPicture->setShadow( + true, + [ + 'X' => 1, + 'Y' => 1, + 'R' => 0, + 'G' => 0, + 'B' => 0, + 'Alpha' => 10, + ] + ); + $settings = [ + 'DisplayValues' => true, + 'DisplaySize' => $fontSize, + 'DisplayR' => 0, + 'DisplayG' => 0, + 'DisplayB' => 0, + 'DisplayOrientation' => ORIENTATION_HORIZONTAL, + 'Gradient' => false, + 'Surrounding' => 30, + 'InnerSurrounding' => 25, + ]; + $myPicture->drawStackedBarChart($settings); + + $legendSettings = [ + 'Mode' => LEGEND_HORIZONTAL, + 'Style' => LEGEND_NOBORDER, + ]; + $myPicture->drawLegend($widthSize / 2, 15, $legendSettings); + + /* Write and save into cache */ + + $myCache->writeToCache($chartHash, $myPicture); + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } + if (!empty($imgPath)) { + $courses_graph[$course_code] = ''; + } + } + } + } // end for + } + + return $graphs; + } + + /** + * This method return a graph containing information about evaluations + * inside courses in sessions, it's used inside get_block method for + * showing it inside dashboard interface. + * + * @return string img html + */ + public function get_evaluations_courses_in_sessions_graph() + { + $graphs = []; + if (!empty($this->sessions)) { + $session_ids = array_keys($this->sessions); + foreach ($session_ids as $session_id) { + $courses_code = array_keys(Tracking::get_courses_list_from_session($session_id)); + $courses_graph = []; + foreach ($courses_code as $course_code) { + $cats = Category::load(null, null, $course_code, null, null, $session_id); + if (isset($cats) && isset($cats[0])) { + $alleval = $cats[0]->get_evaluations(null, true, $course_code); + $alllinks = $cats[0]->get_links(null, true); + $users = GradebookUtils::get_all_users($alleval, $alllinks); + $datagen = new FlatViewDataGenerator($users, $alleval, $alllinks); + $evaluation_sumary = $datagen->getEvaluationSummaryResults(); + if (!empty($evaluation_sumary)) { + $items = array_keys($evaluation_sumary); + $max = $min = $avg = []; + foreach ($evaluation_sumary as $evaluation) { + $max[] = $evaluation['max']; + $min[] = $evaluation['min']; + $avg[] = $evaluation['avg']; + } + // Dataset definition + $dataSet = new pData(); + $dataSet->addPoints($min, 'Serie3'); + $dataSet->addPoints($avg, 'Serie2'); + $dataSet->addPoints($max, 'Serie1'); + $dataSet->addPoints($items, 'Labels'); + + $dataSet->setSerieDescription('Serie1', get_lang('Max')); + $dataSet->setSerieDescription('Serie2', get_lang('Avg')); + $dataSet->setSerieDescription('Serie3', get_lang('Min')); + $dataSet->setAbscissa('Labels'); + $dataSet->setAbscissaName(get_lang('EvaluationName')); + $dataSet->normalize(100, '%'); + $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true); + + // Cache definition + $cachePath = api_get_path(SYS_ARCHIVE_PATH); + $myCache = new pCache( + [ + 'CacheFolder' => substr( + $cachePath, + 0, + strlen($cachePath) - 1 + ), + ] + ); + $chartHash = $myCache->getHash($dataSet); + if ($myCache->isInCache($chartHash)) { + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } else { + /* Create the pChart object */ + $widthSize = $this->bg_width; + $heightSize = $this->bg_height; + $fontSize = 8; + $angle = 50; + + $myPicture = new pImage($widthSize, $heightSize, $dataSet); + + /* Turn of Antialiasing */ + $myPicture->Antialias = false; + + /* Add a border to the picture */ + $myPicture->drawRectangle( + 0, + 0, + $widthSize - 1, + $heightSize - 1, + [ + 'R' => 0, + 'G' => 0, + 'B' => 0, + ] + ); + + /* Set the default font */ + $myPicture->setFontProperties( + [ + 'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf', + 'FontSize' => 10, + ] + ); + + /* Do NOT Write the chart title */ + + /* Define the chart area */ + $myPicture->setGraphArea(50, 30, $widthSize - 20, $heightSize - 100); + + /* Draw the scale */ + $scaleSettings = [ + 'GridR' => 200, + 'GridG' => 200, + 'GridB' => 200, + 'DrawSubTicks' => true, + 'CycleBackground' => true, + 'Mode' => SCALE_MODE_MANUAL, + 'ManualScale' => [ + '0' => [ + 'Min' => 0, + 'Max' => 100, + ], + ], + 'LabelRotation' => $angle, + ]; + $myPicture->drawScale($scaleSettings); + + /* Turn on shadow computing */ + $myPicture->setShadow( + true, + [ + 'X' => 1, + 'Y' => 1, + 'R' => 0, + 'G' => 0, + 'B' => 0, + 'Alpha' => 10, + ] + ); + + /* Draw the chart */ + $myPicture->setShadow( + true, + [ + 'X' => 1, + 'Y' => 1, + 'R' => 0, + 'G' => 0, + 'B' => 0, + 'Alpha' => 10, + ] + ); + $settings = [ + 'DisplayValues' => true, + 'DisplaySize' => $fontSize, + 'DisplayR' => 0, + 'DisplayG' => 0, + 'DisplayB' => 0, + 'DisplayOrientation' => ORIENTATION_HORIZONTAL, + 'Gradient' => false, + 'Surrounding' => 30, + 'InnerSurrounding' => 25, + ]; + $myPicture->drawStackedBarChart($settings); + + $legendSettings = [ + 'Mode' => LEGEND_HORIZONTAL, + 'Style' => LEGEND_NOBORDER, + ]; + $myPicture->drawLegend($widthSize / 2, 15, $legendSettings); + + /* Write and save into cache */ + $myCache->writeToCache($chartHash, $myPicture); + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } + if (!empty($imgPath)) { + $courses_graph[$course_code] = ''; + } + } + } + } + if (!empty($courses_graph)) { + $graphs[$session_id] = $courses_graph; + } + } + } + + return $graphs; + } +} diff --git a/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.info b/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.info new file mode 100644 index 000000000..3607f570a --- /dev/null +++ b/plugin/dashboard/block_evaluation_graph/block_evaluation_graph.info @@ -0,0 +1,6 @@ +name = "Evaluation Graph Block" +controller = "BlockEvaluationGraph" +description = "Display maximum, minimum and average evaluations about links inside assessments tool" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/block_global_info/block_global_info.class.php b/plugin/dashboard/block_global_info/block_global_info.class.php new file mode 100644 index 000000000..a393b8b50 --- /dev/null +++ b/plugin/dashboard/block_global_info/block_global_info.class.php @@ -0,0 +1,152 @@ +user_id = $user_id; + $this->path = 'block_global_info'; + if ($this->is_block_visible_for_user($user_id)) { + //$this->courses = CourseManager::get_courses_followed_by_drh($user_id); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information + * about courses and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 2; + $data = []; + $content = $this->get_content_html(); + $html = '
    +
    '.get_lang('GlobalPlatformInformation').' + +
    +
    + '.$content.' +
    +
    + '; + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_content_html() + { + $global_data = $this->get_global_information_data(); + $content = '

    '.get_lang('GlobalPlatformInformation').'

    '; + $data_table = null; + if (!empty($global_data)) { + $data_table = ''; + $i = 1; + foreach ($global_data as $data) { + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $data_table .= ''; + foreach ($data as $cell) { + $data_table .= ''; + } + $data_table .= ''; + $i++; + } + $data_table .= '
    '.$cell.'
    '; + } else { + $data_table .= get_lang('ThereIsNoInformationAboutThePlatform'); + } + $content .= $data_table; + + return $content; + } + + /** + * Get global information data. + * + * @return array + */ + public function get_global_information_data() + { + // Two-dimensional array with data about the system + $path = api_get_path(WEB_CODE_PATH); + // Check total number of users + $global_info = [ + [get_lang('CountUsers'), ''.Statistics::countUsers().''], + // Check only active users + [get_lang('NumberOfUsersActive'), ''.Statistics::countUsers(null, null, null, true).''], + // Check number of courses + [get_lang('NumberOfCoursesTotal'), ''.Statistics::countCourses().''], + [get_lang('NumberOfCoursesPublic'), ''.Statistics::countCoursesByVisibility([COURSE_VISIBILITY_OPEN_WORLD]).''], + [get_lang('NumberOfCoursesOpen'), ''.Statistics::countCoursesByVisibility([COURSE_VISIBILITY_OPEN_PLATFORM]).''], + [get_lang('NumberOfCoursesPrivate'), ''.Statistics::countCoursesByVisibility([COURSE_VISIBILITY_REGISTERED]).''], + [get_lang('NumberOfCoursesClosed'), ''.Statistics::countCoursesByVisibility([COURSE_VISIBILITY_CLOSED]).''], + [get_lang('NumberOfCoursesHidden'), ''.Statistics::countCoursesByVisibility([COURSE_VISIBILITY_HIDDEN]).''], + ]; + + return $global_info; + } +} diff --git a/plugin/dashboard/block_global_info/block_global_info.info b/plugin/dashboard/block_global_info/block_global_info.info new file mode 100644 index 000000000..13ea3295e --- /dev/null +++ b/plugin/dashboard/block_global_info/block_global_info.info @@ -0,0 +1,6 @@ +name = "Global info Block" +controller = "BlockGlobalInfo" +description = "Display global platform information" +package = Dashboard +version = 1.0 +author = Yannick Warnier diff --git a/plugin/dashboard/block_global_info/index.html b/plugin/dashboard/block_global_info/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_global_info/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_session/block_session.class.php b/plugin/dashboard/block_session/block_session.class.php new file mode 100644 index 000000000..2cef1ef4b --- /dev/null +++ b/plugin/dashboard/block_session/block_session.class.php @@ -0,0 +1,164 @@ +user_id = $user_id; + $this->path = 'block_session'; + if ($this->is_block_visible_for_user($user_id)) { + $this->sessions = SessionManager::get_sessions_followed_by_drh($user_id); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing + * information about sessions and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 2; + $data = []; + $content = $this->get_content_html(); + $content_html = '
    +
    + '.get_lang('SessionsInformation').' + +
    +
    + '.$content.' +
    +
    + '; + + $data['column'] = $column; + $data['content_html'] = $content_html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_content_html() + { + $content = ''; + $sessions = $this->sessions; + $content .= '

    '.get_lang('YourSessionsList').'

    '; + if (count($sessions) > 0) { + $sessions_table = ''; + $sessions_table .= ' + + + + '; + $i = 1; + foreach ($sessions as $session) { + $session_id = intval($session['id']); + $title = $session['name']; + + if (!empty($session['access_start_date'])) { + $dateFrom = api_convert_and_format_date( + $session['access_start_date'], + DATE_FORMAT_SHORT, + date_default_timezone_get() + ); + $dateUntil = api_convert_and_format_date( + $session['access_end_date'], + DATE_FORMAT_SHORT, + date_default_timezone_get() + ); + + $date = vsprintf(get_lang('FromDateXToDateY'), [$dateFrom, $dateUntil]); + } else { + $date = ' - '; + } + + $count_courses_in_session = count(Tracking::get_courses_list_from_session($session_id)); + + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + + $sessions_table .= ' + + + + '; + $i++; + } + $sessions_table .= '
    '.get_lang('Title').''.get_lang('Date').''.get_lang('NbCoursesPerSession').'
    '.$title.''.$date.''.$count_courses_in_session.'
    '; + $content .= $sessions_table; + } else { + $content .= get_lang('ThereIsNoInformationAboutYourSessions'); + } + + if (count($sessions) > 0) { + $content .= ''; + } + + return $content; + } + + /** + * Get number of sessions. + * + * @return int + */ + public function get_number_of_sessions() + { + return count($this->sessions); + } +} diff --git a/plugin/dashboard/block_session/block_session.info b/plugin/dashboard/block_session/block_session.info new file mode 100644 index 000000000..1aef7a03a --- /dev/null +++ b/plugin/dashboard/block_session/block_session.info @@ -0,0 +1,6 @@ +name = "Session Block" +controller = "BlockSession" +description = "Display information about sessions inside platform" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/block_session/index.html b/plugin/dashboard/block_session/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_session/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_student/block_student.class.php b/plugin/dashboard/block_student/block_student.class.php new file mode 100644 index 000000000..ec61fe1dd --- /dev/null +++ b/plugin/dashboard/block_student/block_student.class.php @@ -0,0 +1,259 @@ +user_id = $user_id; + $this->path = 'block_student'; + if ($this->is_block_visible_for_user($user_id)) { + $this->students = UserManager::get_users_followed_by_drh($user_id, STUDENT); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information + * about students and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 1; + $data = []; + $student_content_html = $this->get_students_content_html_for_drh(); + $html = '
    +
    + '.get_lang('StudentsInformationsList').' + +
    +
    + '.$student_content_html.' +
    +
    '; + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_students_content_html_for_platform_admin() + { + $students = $this->students; + $content = '

    '.get_lang('YourStudents').'

    '; + $students_table = null; + if (count($students) > 0) { + $students_table .= ''; + $students_table .= ' + + + + + + + + '; + + $i = 1; + foreach ($students as $student) { + $courses_by_user = CourseManager::get_courses_list_by_user_id($student['user_id'], true); + $count_courses = count($courses_by_user); + $rowspan = $count_courses ? $count_courses + 1 : 2; + + if ($i % 2 == 0) { + $style = ' style="background-color:#F2F2F2" '; + } else { + $style = ' style="background-color:#FFF" '; + } + + $students_table .= ' + + + '; + + // courses information about the student + if (!empty($courses_by_user)) { + foreach ($courses_by_user as $course) { + $course_code = $course['code']; + $courseInfo = api_get_course_info($course_code); + $courseId = $courseInfo['real_id']; + $course_title = $course['title']; + $time = api_time_to_hms(Tracking::get_time_spent_on_the_course($student['user_id'], $courseId)); + $students_table .= ' + + + '; + } + } else { + $students_table .= ' + + '; + } + $i++; + } + $students_table .= '
    '.get_lang('FirstName').''.get_lang('LastName').''.get_lang('CourseInformation').'
    '.get_lang('Courses').''.get_lang('Time').'
    '.$student['firstname'].''.$student['lastname'].'
    '.$course_title.''.$time.'
    '.get_lang('Empty').'
    '; + } else { + $students_table .= get_lang('ThereIsNoInformationAboutYourStudents'); + } + + $content .= $students_table; + + if (count($students) > 0) { + $content .= ''; + } + //$content .= ''; + + return $content; + } + + /** + * @return string + */ + public function get_students_content_html_for_drh() + { + $attendance = new Attendance(); + $students = $this->students; + $content = '

    '.get_lang('YourStudents').'

    '; + $students_table = null; + if (count($students) > 0) { + $students_table .= ''; + $students_table .= ' + + + + '; + $i = 1; + foreach ($students as $student) { + $student_id = $student['user_id']; + $firstname = $student['firstname']; + $lastname = $student['lastname']; + $username = $student['username']; + // get average of faults in attendances by student + $results_faults_avg = $attendance->get_faults_average_inside_courses($student_id); + if (!empty($results_faults_avg)) { + $attendances_faults_avg = ''.$results_faults_avg['faults'].'/'.$results_faults_avg['total'].' ('.$results_faults_avg['porcent'].'%)'; + } else { + $attendances_faults_avg = '0%'; + } + + $courses_by_user = CourseManager::get_courses_list_by_user_id($student_id, true); + $evaluations_avg = 0; + $score = $weight = 0; + foreach ($courses_by_user as $course) { + $course_code = $course['code']; + $cats = Category::load( + null, + null, + $course_code, + null, + null, + null, + false + ); + $scoretotal = []; + if (isset($cats) && isset($cats[0])) { + $scoretotal = $cats[0]->calc_score($student_id, null, $course_code); + } + + if (!empty($scoretotal)) { + $score += $scoretotal[0]; + $weight += $scoretotal[1]; + } + } + + if (!empty($weight)) { + $evaluations_avg = ''.round($score, 2).'/'.round($weight, 2).'('.round(($score / $weight) * 100, 2).' %)'; + } + + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $students_table .= ' + + + + '; + + $i++; + } + $students_table .= '
    '.get_lang('User').''.get_lang('AttendancesFaults').''.get_lang('Evaluations').'
    '.api_get_person_name($firstname, $lastname).' ('.$username.')'.$attendances_faults_avg.''.$evaluations_avg.'
    '; + } else { + $students_table .= get_lang('ThereIsNoInformationAboutYourStudents'); + } + + $content .= $students_table; + + if (count($students) > 0) { + $content .= ''; + } + //$content .= ''; + + return $content; + } + + /** + * Get number of students. + * + * @return int + */ + public function get_number_of_students() + { + return count($this->students); + } +} diff --git a/plugin/dashboard/block_student/block_student.info b/plugin/dashboard/block_student/block_student.info new file mode 100644 index 000000000..65897b9ce --- /dev/null +++ b/plugin/dashboard/block_student/block_student.info @@ -0,0 +1,6 @@ +name = "Student Block" +controller = "BlockStudent" +description = "Display information about students inside platform" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/block_student/index.html b/plugin/dashboard/block_student/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_student/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_student_graph/block_student_graph.class.php b/plugin/dashboard/block_student_graph/block_student_graph.class.php new file mode 100644 index 000000000..560e9404c --- /dev/null +++ b/plugin/dashboard/block_student_graph/block_student_graph.class.php @@ -0,0 +1,263 @@ + + */ +use CpChart\Cache as pCache; +use CpChart\Data as pData; +use CpChart\Image as pImage; + +/** + * This class is used like controller for student graph block plugin, + * the class name must be registered inside path.info file + * (e.g: controller = "BlockStudentGraph"), so dashboard controller will be instantiate it. + * + * @package chamilo.dashboard + */ +class BlockStudentGraph extends Block +{ + private $user_id; + private $students; + private $path; + private $permission = [DRH]; + + /** + * Constructor. + */ + public function __construct($user_id) + { + $this->user_id = $user_id; + $this->path = 'block_student_graph'; + if ($this->is_block_visible_for_user($user_id)) { + /*if (api_is_platform_admin()) { + $this->students = UserManager::get_user_list(array('status' => STUDENT)); + } else if (api_is_drh()) {*/ + $this->students = UserManager::get_users_followed_by_drh($user_id, STUDENT); + //} + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information about students + * and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for being used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 1; + $data = []; + $students_attendance_graph = $this->get_students_attendance_graph(); + + $html = '
    +
    + '.get_lang('StudentsInformationsGraph').' + +
    +
    +
    '.get_lang('AttendancesFaults').'
    + '.$students_attendance_graph.' +
    +
    '; + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a graph containing information about students evaluation, + * it's used inside get_block method for showing it inside dashboard interface. + * + * @return string img html + */ + public function get_students_attendance_graph() + { + $students = $this->students; + $attendance = new Attendance(); + + // get data + $attendances_faults_avg = []; + if (is_array($students) && count($students) > 0) { + foreach ($students as $student) { + $student_id = $student['user_id']; + //$student_info = api_get_user_info($student_id); + // get average of faults in attendances by student + $results_faults_avg = $attendance->get_faults_average_inside_courses($student_id); + + if (!empty($results_faults_avg)) { + $attendances_faults_avg[$student['lastname']] = $results_faults_avg['porcent']; + } else { + $attendances_faults_avg[$student['lastname']] = 0; + } + } + } + + arsort($attendances_faults_avg); + $usernames = array_keys($attendances_faults_avg); + + $faults = []; + foreach ($usernames as $username) { + $faults[] = $attendances_faults_avg[$username]; + } + + $graph = ''; + $img_file = ''; + if (is_array($usernames) && count($usernames) > 0) { + // Defining data + $dataSet = new pData(); + $dataSet->addPoints($faults, 'Serie1'); + $dataSet->addPoints($usernames, 'Labels'); + $dataSet->setSerieDescription('Series1', get_lang('Average')); + $dataSet->setSerieDescription('Labels', get_lang('User')); + $dataSet->setAbscissa('Labels'); + $dataSet->setAbscissaName(get_lang('User')); + $dataSet->setAxisName(0, get_lang('Attendance')); + $palette = [ + '0' => ['R' => 186, 'G' => 206, 'B' => 151, 'Alpha' => 100], + '1' => ['R' => 210, 'G' => 148, 'B' => 147, 'Alpha' => 100], + '2' => ['R' => 148, 'G' => 170, 'B' => 208, 'Alpha' => 100], + '3' => ['R' => 221, 'G' => 133, 'B' => 61, 'Alpha' => 100], + '4' => ['R' => 65, 'G' => 153, 'B' => 176, 'Alpha' => 100], + '5' => ['R' => 114, 'G' => 88, 'B' => 144, 'Alpha' => 100], + '6' => ['R' => 138, 'G' => 166, 'B' => 78, 'Alpha' => 100], + '7' => ['R' => 171, 'G' => 70, 'B' => 67, 'Alpha' => 100], + '8' => ['R' => 69, 'G' => 115, 'B' => 168, 'Alpha' => 100], + ]; + // Cache definition + $cachePath = api_get_path(SYS_ARCHIVE_PATH); + $myCache = new pCache( + [ + 'CacheFolder' => substr( + $cachePath, + 0, + strlen($cachePath) - 1 + ), + ] + ); + $chartHash = $myCache->getHash($dataSet); + if ($myCache->isInCache($chartHash)) { + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } else { + $maxCounts = max(count($usernames), count($faults)); + if ($maxCounts < 5) { + $heightSize = 200; + } else { + $heightSize = $maxCounts * 40; + } + + /* Create the pChart object */ + $widthSize = 480; + $angle = 40; + + $myPicture = new pImage($widthSize, $heightSize, $dataSet); + + /* Turn of Antialiasing */ + $myPicture->Antialias = false; + + /* Add a border to the picture */ + $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]); + + /* Set the default font */ + $myPicture->setFontProperties( + [ + 'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf', + 'FontSize' => 10, + ] + ); + + /* Do NOT Write the chart title */ + + /* Define the chart area */ + $myPicture->setGraphArea(80, 40, $widthSize - 20, $heightSize - 40); + + /* Draw the scale */ + $scaleSettings = [ + 'GridR' => 200, + 'GridG' => 200, + 'GridB' => 200, + 'DrawSubTicks' => true, + 'CycleBackground' => true, + 'Mode' => SCALE_MODE_ADDALL_START0, + 'Pos' => SCALE_POS_TOPBOTTOM, + 'DrawXLines' => false, + 'LabelRotation' => $angle, + ]; + + $myPicture->drawScale($scaleSettings); + + /* Turn on shadow computing */ + $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + + /* Draw the chart */ + $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $settings = [ + 'OverrideColors' => $palette, + 'Gradient' => false, + 'GradientMode' => GRADIENT_SIMPLE, + 'DisplayPos' => LABEL_POS_TOP, + 'DisplayValues' => true, + 'DisplayR' => 0, + 'DisplayG' => 0, + 'DisplayB' => 0, + 'DisplayShadow' => true, + 'Surrounding' => 10, + ]; + $myPicture->drawBarChart($settings); + + /* Write and save into cache */ + $myCache->writeToCache($chartHash, $myPicture); + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } + $graph = ''; + } else { + $graph = '

    '.api_convert_encoding(get_lang('GraphicNotAvailable'), 'UTF-8').'

    '; + } + + return $graph; + } + + /** + * Get number of students. + * + * @return int + */ + public function get_number_of_students() + { + return count($this->students); + } +} diff --git a/plugin/dashboard/block_student_graph/block_student_graph.info b/plugin/dashboard/block_student_graph/block_student_graph.info new file mode 100644 index 000000000..e4d29bc3f --- /dev/null +++ b/plugin/dashboard/block_student_graph/block_student_graph.info @@ -0,0 +1,6 @@ +name = "Student Graph Block" +controller = "BlockStudentGraph" +description = "Display information about students inside platform with graph" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/block_teacher/block_teacher.class.php b/plugin/dashboard/block_teacher/block_teacher.class.php new file mode 100644 index 000000000..f22aa86f2 --- /dev/null +++ b/plugin/dashboard/block_teacher/block_teacher.class.php @@ -0,0 +1,212 @@ +user_id = $user_id; + $this->path = 'block_teacher'; + if ($this->is_block_visible_for_user($user_id)) { + $this->teachers = UserManager::get_users_followed_by_drh($user_id, COURSEMANAGER); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information about + * teachers and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from + * dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 1; + $data = []; + $teacher_content_html = $this->get_teachers_content_html_for_drh(); + $html = ' +
    +
    + '.get_lang('TeachersInformationsList').' + +
    +
    + '.$teacher_content_html.' +
    +
    + '; + + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method + * for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_teachers_content_html_for_platform_admin() + { + $teachers = $this->teachers; + $content = '

    '.get_lang('YourTeachers').'

    '; + + $teachers_table = null; + if (count($teachers) > 0) { + $teachers_table .= ''; + $teachers_table .= ' + + + + + + '; + $i = 1; + foreach ($teachers as $teacher) { + $teacher_id = $teacher['user_id']; + $firstname = $teacher['firstname']; + $lastname = $teacher['lastname']; + $username = $teacher['username']; + + $time_on_platform = api_time_to_hms(Tracking::get_time_spent_on_the_platform($teacher_id)); + $last_connection = Tracking::get_last_connection_date($teacher_id); + + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $teachers_table .= ' + + + + + + '; + $i++; + } + $teachers_table .= '
    '.get_lang('User').''.get_lang('TimeSpentOnThePlatform').''.get_lang('LastConnexion').'
    '.api_get_person_name($firstname, $lastname).' ('.$username.')'.$time_on_platform.''.$last_connection.'
    '; + } else { + $teachers_table .= get_lang('ThereIsNoInformationAboutYourTeachers'); + } + + $content .= $teachers_table; + + if (count($teachers) > 0) { + $content .= ''; + } + + return $content; + } + + public function get_teachers_content_html_for_drh() + { + $teachers = $this->teachers; + $content = '

    '.get_lang('YourTeachers').'

    '; + $teachers_table = null; + if (count($teachers) > 0) { + $a_last_week = get_last_week(); + $last_week = date('Y-m-d', $a_last_week[0]).' '.get_lang('To').' '.date('Y-m-d', $a_last_week[6]); + + $teachers_table .= ''; + $teachers_table .= ' + + + + + '; + + $i = 1; + foreach ($teachers as $teacher) { + $teacher_id = $teacher['user_id']; + $firstname = $teacher['firstname']; + $lastname = $teacher['lastname']; + $username = $teacher['username']; + $time_on_platform = api_time_to_hms( + Tracking::get_time_spent_on_the_platform($teacher_id, true) + ); + + if ($i % 2 == 0) { + $class_tr = 'row_odd'; + } else { + $class_tr = 'row_even'; + } + $teachers_table .= ' + + + '; + + $i++; + } + $teachers_table .= '
    '.get_lang('User').''.get_lang('TimeSpentLastWeek').'
    '.$last_week.'
    '.api_get_person_name($firstname, $lastname).' ('.$username.')'.$time_on_platform.'
    '; + } else { + $teachers_table .= get_lang('ThereIsNoInformationAboutYourTeachers'); + } + $content .= $teachers_table; + if (count($teachers) > 0) { + $content .= ''; + } + + return $content; + } + + /** + * Get number of teachers. + * + * @return int + */ + public function get_number_of_teachers() + { + return count($this->teachers); + } +} diff --git a/plugin/dashboard/block_teacher/block_teacher.info b/plugin/dashboard/block_teacher/block_teacher.info new file mode 100644 index 000000000..af4394aa5 --- /dev/null +++ b/plugin/dashboard/block_teacher/block_teacher.info @@ -0,0 +1,6 @@ +name = "Teacher Block" +controller = "BlockTeacher" +description = "Display information about teachers inside platform" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/block_teacher/index.html b/plugin/dashboard/block_teacher/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/block_teacher/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/dashboard/block_teacher_graph/block_teacher_graph.class.php b/plugin/dashboard/block_teacher_graph/block_teacher_graph.class.php new file mode 100644 index 000000000..9dfbc629b --- /dev/null +++ b/plugin/dashboard/block_teacher_graph/block_teacher_graph.class.php @@ -0,0 +1,221 @@ +user_id = $user_id; + $this->path = 'block_teacher_graph'; + if ($this->is_block_visible_for_user($user_id)) { + $this->teachers = UserManager::get_users_followed_by_drh($user_id, COURSEMANAGER); + } + } + + /** + * This method check if a user is allowed to see the block inside dashboard interface. + * + * @param int User id + * + * @return bool Is block visible for user + */ + public function is_block_visible_for_user($user_id) + { + $user_info = api_get_user_info($user_id); + $user_status = $user_info['status']; + $is_block_visible_for_user = false; + if (UserManager::is_admin($user_id) || in_array($user_status, $this->permission)) { + $is_block_visible_for_user = true; + } + + return $is_block_visible_for_user; + } + + /** + * This method return content html containing information about teachers and its position for showing it inside dashboard interface + * it's important to use the name 'get_block' for beeing used from dashboard controller. + * + * @return array column and content html + */ + public function get_block() + { + global $charset; + $column = 1; + $data = []; + $teacher_information_graph = $this->get_teachers_information_graph(); + $html = ' +
    +
    '.get_lang('TeachersInformationsGraph').' + +
    +
    +
    '.get_lang('TimeSpentOnThePlatformLastWeekByDay').'
    + '.$teacher_information_graph.' +
    +
    + '; + + $data['column'] = $column; + $data['content_html'] = $html; + + return $data; + } + + /** + * This method return a content html, it's used inside get_block method for showing it inside dashboard interface. + * + * @return string content html + */ + public function get_teachers_information_graph() + { + $teachers = $this->teachers; + $graph = ''; + $user_ids = array_keys($teachers); + $a_last_week = get_last_week(); + + if (is_array($user_ids) && count($user_ids) > 0) { + $dataSet = new pData(); + foreach ($user_ids as $user_id) { + $teacher_info = api_get_user_info($user_id); + $username = $teacher_info['username']; + $time_by_days = []; + foreach ($a_last_week as $day) { + // day is received as y-m-d 12:00:00 + $start_date = api_get_utc_datetime($day); + $end_date = api_get_utc_datetime($day + (3600 * 24 - 1)); + + $time_on_platform_by_day = Tracking::get_time_spent_on_the_platform( + $user_id, + 'custom', + $start_date, + $end_date + ); + $hours = floor($time_on_platform_by_day / 3600); + $min = floor(($time_on_platform_by_day - ($hours * 3600)) / 60); + $time_by_days[] = $min; + } + $dataSet->addPoints($time_by_days, $username); + } + + $last_week = date('Y-m-d', $a_last_week[0]).' '.get_lang('To').' '.date('Y-m-d', $a_last_week[6]); + $days_on_week = []; + foreach ($a_last_week as $weekday) { + $days_on_week[] = date('d/m', $weekday); + } + + $dataSet->addPoints($days_on_week, 'Days'); + $dataSet->setAbscissaName($last_week); + $dataSet->setAxisName(0, get_lang('Minutes')); + $dataSet->setAbscissa('Days'); + $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true); + + // Cache definition + $cachePath = api_get_path(SYS_ARCHIVE_PATH); + $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]); + $chartHash = $myCache->getHash($dataSet); + if ($myCache->isInCache($chartHash)) { + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } else { + /* Create the pChart object */ + $widthSize = 440; + $heightSize = 350; + $angle = 50; + $myPicture = new pImage($widthSize, $heightSize, $dataSet); + + /* Turn of Antialiasing */ + $myPicture->Antialias = false; + + /* Add a border to the picture */ + $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]); + + /* Set the default font */ + $myPicture->setFontProperties(['FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf', 'FontSize' => 10]); + + /* Do NOT Write the chart title */ + + /* Define the chart area */ + $myPicture->setGraphArea(40, 40, $widthSize - 20, $heightSize - 80); + + /* Draw the scale */ + $scaleSettings = [ + 'GridR' => 200, + 'GridG' => 200, + 'GridB' => 200, + 'DrawSubTicks' => true, + 'CycleBackground' => true, + 'Mode' => SCALE_MODE_ADDALL_START0, + 'LabelRotation' => $angle, + ]; + + $myPicture->drawScale($scaleSettings); + + /* Turn on shadow computing */ + $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + + /* Draw the chart */ + $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]); + $settings = [ + 'DisplayValues' => true, + 'DisplayR' => 0, + 'DisplayG' => 0, + 'DisplayB' => 0, + ]; + $myPicture->drawFilledSplineChart($settings); + $myPicture->drawLegend(40, 20, ['Mode' => LEGEND_HORIZONTAL]); + + /* Write and save into cache */ + $myCache->writeToCache($chartHash, $myPicture); + $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash; + $myCache->saveFromCache($chartHash, $imgPath); + $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash; + } + $graph = ''; + } else { + $graph = '

    '.api_convert_encoding(get_lang('GraphicNotAvailable'), 'UTF-8').'

    '; + } + + return $graph; + } + + /** + * Get number of teachers. + * + * @return int + */ + public function get_number_of_teachers() + { + return count($this->teachers); + } +} diff --git a/plugin/dashboard/block_teacher_graph/block_teacher_graph.info b/plugin/dashboard/block_teacher_graph/block_teacher_graph.info new file mode 100644 index 000000000..b0ffc7f20 --- /dev/null +++ b/plugin/dashboard/block_teacher_graph/block_teacher_graph.info @@ -0,0 +1,6 @@ +name = "Teacher Graph Block" +controller = "BlockTeacherGraph" +description = "Display information about teachers inside platform with graph" +package = Dashboard +version = 1.0 +author = Christian Fasanando \ No newline at end of file diff --git a/plugin/dashboard/css/default.css b/plugin/dashboard/css/default.css new file mode 100644 index 000000000..26533e3ae --- /dev/null +++ b/plugin/dashboard/css/default.css @@ -0,0 +1,80 @@ +/* Colors */ +.color-yellow {background:#f2bc00;} +.color-red {background:#dd0000;} +.color-blue {background:#00A9E0;} +.color-white {background:#dfdfdf;} +.color-orange {background:#f66e00;} +.color-green {background:#8dc100;} +.color-yellow h3, +.color-white h3, +.color-green h3 + {color:#000;} +.color-red h3, +.color-blue h3, +.color-orange h3 { + color: #FFF; +} + +/* End Colors */ + +.widget { + margin: 30px 20px 0 20px; + padding: 2px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; +} +.widget .widget-head { + color: #000; + overflow: hidden; + width: 100%; + height: 30px; + line-height: 30px; +} +.widget .widget-head h3 { + padding: 0 5px; + float: left; +} +.widget .widget-content { + background: #FFF url(img/widget-content-bg.png) repeat-x; + padding: 0 5px; + color: #000; + -moz-border-radius-bottomleft: 2px; + -moz-border-radius-bottomright: 2px; + -webkit-border-bottom-left-radius: 2px; + -webkit-border-bottom-right-radius: 2px; + line-height: 1.2em; + overflow: hidden; +} +.widget .widget-content p { + padding: 0.8em 0; + border-bottom: 1px solid #666; +} +.widget .widget-content pre { + padding: 0.5em 5px; + color: #EEE; + font-size: 12px; +} +.widget .widget-content ul { + padding: 5px 0 5px 20px; + list-style: disc; +} +.widget .widget-content ul li {padding: 3px 0;} +.widget .widget-content ul.images { + padding: 7px 0 0 0; + list-style: none; + height: 1%; +} +.widget .widget-content ul.images li { + display: inline; + float: left; +} +.widget .widget-content ul.images img { + display: inline; + float: left; + margin: 0 0 7px 7px; +} + +.widget-actions {text-align:right;margin-right:5px;margin-top:5px;} +.widget-actions img { + margin-bottom: 15px; +} diff --git a/plugin/dashboard/index.html b/plugin/dashboard/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/dashboard/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/date/README.md b/plugin/date/README.md new file mode 100644 index 000000000..2290eb9b1 --- /dev/null +++ b/plugin/date/README.md @@ -0,0 +1,8 @@ +Date plugin +=== + +This plugin is more of a demo and a plugin example than a real, functional plugin. + +It will show the current date in an area of your choice. + +The .act.php and .dsp.php files are just remains of a time where we didn't use the Twig templating system. \ No newline at end of file diff --git a/plugin/date/index.act.php b/plugin/date/index.act.php new file mode 100644 index 000000000..6438e6828 --- /dev/null +++ b/plugin/date/index.act.php @@ -0,0 +1,22 @@ + "en_US", + "french" => "fr_BE", + "dutch" => "nl_NL", + "german" => "de_DE", + "japanese" => "ja_JP", + "danish" => "da_DK", +]; +if (!empty($_SESSION['user_language_choice']) && !empty($convert_lang_to_code[$_SESSION['user_language_choice']])) { + $code = $convert_lang_to_code[$_SESSION['user_language_choice']]; + $locale = setlocale(LC_TIME, $code); +} +$date = strftime('%c'); diff --git a/plugin/date/index.dsp.php b/plugin/date/index.dsp.php new file mode 100644 index 000000000..2b2223ad9 --- /dev/null +++ b/plugin/date/index.dsp.php @@ -0,0 +1,10 @@ +'.strip_tags($date).''; diff --git a/plugin/date/index.php b/plugin/date/index.php new file mode 100644 index 000000000..000eb2690 --- /dev/null +++ b/plugin/date/index.php @@ -0,0 +1,11 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Yannick Warnier + */ +/** + * Plugin details (must be present). + */ +//the plugin title +$plugin_info['title'] = 'Date'; +//the comments that go with the plugin +$plugin_info['comment'] = "Multinational date display"; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Yannick Warnier'; diff --git a/plugin/date/start.php b/plugin/date/start.php new file mode 100644 index 000000000..fda112586 --- /dev/null +++ b/plugin/date/start.php @@ -0,0 +1,10 @@ + 'boolean', + ] + ); + } + + /** + * @return DictionaryPlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Installation process. + */ + public function install() + { + $sql = "CREATE TABLE IF NOT EXISTS plugin_dictionary ( + id INT NOT NULL AUTO_INCREMENT, + term VARCHAR(255) NOT NULL, + definition LONGTEXT NOT NULL, + PRIMARY KEY (id)); + "; + Database::query($sql); + } + + /** + * Uninstall process. + */ + public function uninstall() + { + $sql = "DROP TABLE IF EXISTS plugin_dictionary"; + Database::query($sql); + } +} diff --git a/plugin/dictionary/index.php b/plugin/dictionary/index.php new file mode 100644 index 000000000..6ee8d4ba2 --- /dev/null +++ b/plugin/dictionary/index.php @@ -0,0 +1,2 @@ +install(); diff --git a/plugin/dictionary/lang/english.php b/plugin/dictionary/lang/english.php new file mode 100644 index 000000000..2786b5758 --- /dev/null +++ b/plugin/dictionary/lang/english.php @@ -0,0 +1,6 @@ +get_info(); diff --git a/plugin/dictionary/terms.php b/plugin/dictionary/terms.php new file mode 100644 index 000000000..cdfb3f248 --- /dev/null +++ b/plugin/dictionary/terms.php @@ -0,0 +1,82 @@ +addText('term', $plugin->get_lang('Term'), true); +$form->addTextarea('definition', $plugin->get_lang('Definition'), [], true); +//$form->addHtmlEditor('definition', get_lang('Definition'), true); +$form->addButtonSave(get_lang('Save')); + +switch ($action) { + case 'add': + if ($form->validate()) { + $values = $form->getSubmitValues(); + $params = [ + 'term' => $values['term'], + 'definition' => $values['definition'], + ]; + $result = Database::insert($table, $params); + if ($result) { + Display::addFlash(Display::return_message(get_lang('Added'))); + } + header('Location: '.api_get_self()); + exit; + } + break; + case 'edit': + $form->setDefaults($term); + if ($form->validate()) { + $values = $form->getSubmitValues(); + $params = [ + 'term' => $values['term'], + 'definition' => $values['definition'], + ]; + Database::update($table, $params, ['id = ?' => $id]); + Display::addFlash(Display::return_message(get_lang('Updated'))); + + header('Location: '.api_get_self()); + exit; + } + break; + case 'delete': + if (!empty($term)) { + Database::delete($table, ['id = ?' => $id]); + Display::addFlash(Display::return_message(get_lang('Deleted'))); + header('Location: '.api_get_self()); + exit; + } + break; +} + +$tpl = new Template($plugin->get_lang('plugin_title')); +$tpl->assign('terms', $terms); +$tpl->assign('form', $form->returnForm()); +$content = $tpl->fetch('/'.$plugin->get_name().'/view/terms.html.twig'); +// Assign into content +$tpl->assign('content', $content); +// Display +$tpl->display_one_col_template(); diff --git a/plugin/dictionary/view/terms.html.twig b/plugin/dictionary/view/terms.html.twig new file mode 100644 index 000000000..99d28fdfe --- /dev/null +++ b/plugin/dictionary/view/terms.html.twig @@ -0,0 +1,34 @@ + +{{ form }} + + + + + + + +{% for term in terms %} + + + + + +{% endfor %} +
    + {{ 'Term' | get_plugin_lang('DictionaryPlugin') }} + + {{ 'Definition' | get_plugin_lang('DictionaryPlugin') }} + + {{ 'Actions' | get_lang }} +
    + {{ term.term }} + + {{ term.definition }} + + + {{ 'Edit'|get_lang }} + + + {{ 'Delete'|get_lang }} + +
    \ No newline at end of file diff --git a/plugin/embedregistry/EmbedRegistryPlugin.php b/plugin/embedregistry/EmbedRegistryPlugin.php new file mode 100644 index 000000000..d79a64e32 --- /dev/null +++ b/plugin/embedregistry/EmbedRegistryPlugin.php @@ -0,0 +1,250 @@ + 'boolean', + self::SETTING_TITLE => 'text', + self::SETTING_EXTERNAL_URL => 'text', + ] + ); + } + + /** + * @return string + */ + public function getToolTitle() + { + $title = $this->get(self::SETTING_TITLE); + + if (!empty($title)) { + return $title; + } + + return $this->get_title(); + } + + /** + * @return EmbedRegistryPlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @throws \Doctrine\ORM\Tools\ToolsException + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist([self::TBL_EMBED])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Embed::class), + ] + ); + } + + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TBL_EMBED])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Embed::class), + ] + ); + } + + /** + * @return EmbedRegistryPlugin + */ + public function performActionsAfterConfigure() + { + $em = Database::getManager(); + + $this->deleteCourseToolLinks(); + + if ('true' === $this->get(self::SETTING_ENABLED)) { + $courses = $em->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c')->getResult(); + + foreach ($courses as $course) { + $this->createLinkToCourseTool($this->getToolTitle(), $course['id']); + } + } + + return $this; + } + + /** + * @param int $courseId + */ + public function doWhenDeletingCourse($courseId) + { + Database::getManager() + ->createQuery('DELETE FROM ChamiloPluginBundle:EmbedRegistry\Embed e WHERE e.course = :course') + ->execute(['course' => (int) $courseId]); + } + + /** + * @param int $sessionId + */ + public function doWhenDeletingSession($sessionId) + { + Database::getManager() + ->createQuery('DELETE FROM ChamiloPluginBundle:EmbedRegistry\Embed e WHERE e.session = :session') + ->execute(['session' => (int) $sessionId]); + } + + /** + * @throws \Doctrine\ORM\NonUniqueResultException + * + * @return Embed + */ + public function getCurrentEmbed(Course $course, Session $session = null) + { + $embedRepo = Database::getManager()->getRepository('ChamiloPluginBundle:EmbedRegistry\Embed'); + $qb = $embedRepo->createQueryBuilder('e'); + $query = $qb + ->where('e.displayStartDate <= :now') + ->andWhere('e.displayEndDate >= :now') + ->andWhere( + $qb->expr()->eq('e.course', $course->getId()) + ); + + $query->andWhere( + $session + ? $qb->expr()->eq('e.session', $session->getId()) + : $qb->expr()->isNull('e.session') + ); + + $query = $query + ->orderBy('e.displayStartDate', 'DESC') + ->setMaxResults(1) + ->setParameters(['now' => api_get_utc_datetime(null, false, true)]) + ->getQuery(); + + return $query->getOneOrNullResult(); + } + + /** + * @return string + */ + public function formatDisplayDate(Embed $embed) + { + $startDate = sprintf( + '', + $embed->getDisplayStartDate()->format(DateTime::W3C), + api_convert_and_format_date($embed->getDisplayStartDate()) + ); + $endDate = sprintf( + '', + $embed->getDisplayEndDate()->format(DateTime::W3C), + api_convert_and_format_date($embed->getDisplayEndDate()) + ); + + return sprintf(get_lang('FromDateXToDateY'), $startDate, $endDate); + } + + /** + * @return string + */ + public function getViewUrl(Embed $embed) + { + return api_get_path(WEB_PLUGIN_PATH).'embedregistry/view.php?id='.$embed->getId().'&'.api_get_cidreq(); + } + + /** + * @throws \Doctrine\ORM\Query\QueryException + * + * @return int + */ + public function getMembersCount(Embed $embed) + { + $dql = 'SELECT COUNT(DISTINCT tea.accessUserId) FROM ChamiloCoreBundle:TrackEAccess tea + WHERE + tea.accessTool = :tool AND + (tea.accessDate >= :start_date AND tea.accessDate <= :end_date) AND + tea.cId = :course'; + + $params = [ + 'tool' => 'plugin_'.$this->get_name(), + 'start_date' => $embed->getDisplayStartDate(), + 'end_date' => $embed->getDisplayEndDate(), + 'course' => $embed->getCourse(), + ]; + + if ($embed->getSession()) { + $dql .= ' AND tea.accessSessionId = :session '; + + $params['session'] = $embed->getSession(); + } + + $count = Database::getManager() + ->createQuery($dql) + ->setParameters($params) + ->getSingleScalarResult(); + + return $count; + } + + public function saveEventAccessTool() + { + $tableAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS); + $params = [ + 'access_user_id' => api_get_user_id(), + 'c_id' => api_get_course_int_id(), + 'access_tool' => 'plugin_'.$this->get_name(), + 'access_date' => api_get_utc_datetime(), + 'access_session_id' => api_get_session_id(), + 'user_ip' => api_get_real_ip(), + ]; + Database::insert($tableAccess, $params); + } + + private function deleteCourseToolLinks() + { + Database::getManager() + ->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link') + ->execute(['category' => 'plugin', 'link' => 'embedregistry/start.php%']); + } +} diff --git a/plugin/embedregistry/Entity/Embed.php b/plugin/embedregistry/Entity/Embed.php new file mode 100644 index 000000000..313305b28 --- /dev/null +++ b/plugin/embedregistry/Entity/Embed.php @@ -0,0 +1,198 @@ +id; + } + + /** + * @param int $id + * + * @return Embed + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * @param $title + * + * @return Embed + */ + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + /** + * @return \DateTime + */ + public function getDisplayStartDate() + { + return $this->displayStartDate; + } + + /** + * @return Embed + */ + public function setDisplayStartDate(\DateTime $displayStartDate) + { + $this->displayStartDate = $displayStartDate; + + return $this; + } + + /** + * @return \DateTime + */ + public function getDisplayEndDate() + { + return $this->displayEndDate; + } + + /** + * @return Embed + */ + public function setDisplayEndDate(\DateTime $displayEndDate) + { + $this->displayEndDate = $displayEndDate; + + return $this; + } + + /** + * @return string + */ + public function getHtmlCode() + { + return $this->htmlCode; + } + + /** + * @param string $htmlCode + * + * @return Embed + */ + public function setHtmlCode($htmlCode) + { + $this->htmlCode = $htmlCode; + + return $this; + } + + /** + * @return Course + */ + public function getCourse() + { + return $this->course; + } + + /** + * @return Embed + */ + public function setCourse(Course $course) + { + $this->course = $course; + + return $this; + } + + /** + * @return Session|null + */ + public function getSession() + { + return $this->session; + } + + /** + * @return Embed + */ + public function setSession(Session $session = null) + { + $this->session = $session; + + return $this; + } +} diff --git a/plugin/embedregistry/README.md b/plugin/embedregistry/README.md new file mode 100644 index 000000000..386cd450d --- /dev/null +++ b/plugin/embedregistry/README.md @@ -0,0 +1,14 @@ +# Embed Registry + +Add a new tool in every course to offer embedded content from external sources +*and* track accesses to the content itself (so you can monitor the interest). + +## Set up + +Prior to installing/uninstalling this plugin, make sure the src/Chamilo/PluginBundle/Entity folder is +temporarily writeable by the web server. + +Enable the plugin. Suggest a default source for all the embedded content, then let teachers define the +content (usually videos) they want to embed into their courses. + +Time periods allow teachers to schedule the highlighting of some content above others at specific dates. diff --git a/plugin/embedregistry/install.php b/plugin/embedregistry/install.php new file mode 100644 index 000000000..19b924077 --- /dev/null +++ b/plugin/embedregistry/install.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/embedregistry/lang/english.php b/plugin/embedregistry/lang/english.php new file mode 100644 index 000000000..a6129f035 --- /dev/null +++ b/plugin/embedregistry/lang/english.php @@ -0,0 +1,22 @@ +get_info(); diff --git a/plugin/embedregistry/start.php b/plugin/embedregistry/start.php new file mode 100644 index 000000000..5704fb00a --- /dev/null +++ b/plugin/embedregistry/start.php @@ -0,0 +1,309 @@ +get(EmbedRegistryPlugin::SETTING_ENABLED)) { + api_not_allowed(true); +} + +$isAllowedToEdit = api_is_allowed_to_edit(true); + +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null; + +$em = Database::getManager(); +$embedRepo = $em->getRepository('ChamiloPluginBundle:EmbedRegistry\Embed'); + +$course = api_get_course_entity(api_get_course_int_id()); +$session = api_get_session_entity(api_get_session_id()); + +$actions = []; + +$view = new Template($plugin->getToolTitle()); +$view->assign('is_allowed_to_edit', $isAllowedToEdit); + +switch ($action) { + case 'add': + if (!$isAllowedToEdit) { + api_not_allowed(true); + } + + $actions[] = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_self() + ); + + $form = new FormValidator('frm_edit'); + $form->addText( + 'title', + [get_lang('Title'), $plugin->get_lang('EmbedTitleHelp')], + true + ); + $form->addDateRangePicker( + 'range', + [get_lang('DateRange'), $plugin->get_lang('EmbedDateRangeHelp')] + ); + $form->addTextarea( + 'html_code', + [$plugin->get_lang('HtmlCode'), $plugin->get_lang('HtmlCodeHelp')], + ['rows' => 5], + true + ); + $form->addButtonUpdate(get_lang('Add')); + $form->addHidden('action', 'add'); + + if ($form->validate()) { + $values = $form->exportValues(); + + $startDate = api_get_utc_datetime($values['range_start'], false, true); + $endDate = api_get_utc_datetime($values['range_end'], false, true); + + $embed = new Embed(); + $embed + ->setTitle($values['title']) + ->setDisplayStartDate($startDate) + ->setDisplayEndDate($endDate) + ->setHtmlCode($values['html_code']) + ->setCourse($course) + ->setSession($session); + + $em->persist($embed); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('Added'), 'success') + ); + + header('Location: '.api_get_self()); + exit; + } + + $view->assign('header', $plugin->get_lang('CreateEmbeddable')); + $view->assign('form', $form->returnForm()); + + $externalUrl = $plugin->get(EmbedRegistryPlugin::SETTING_EXTERNAL_URL); + + if (!empty($externalUrl)) { + $view->assign('external_url', $externalUrl); + } + break; + case 'edit': + if (!$isAllowedToEdit) { + api_not_allowed(true); + } + + $actions[] = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_self() + ); + + $embedId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; + + if (!$embedId) { + break; + } + + /** @var Embed|null $embed */ + $embed = $embedRepo->find($embedId); + + if (!$embed) { + Display::addFlash(Display::return_message($plugin->get_lang('ContentNotFound'), 'danger')); + + break; + } + + $form = new FormValidator('frm_edit'); + $form->addText('title', get_lang('Title'), true); + $form->addDateRangePicker('range', get_lang('DateRange')); + $form->addTextarea('html_code', $plugin->get_lang('HtmlCode'), ['rows' => 5], true); + $form->addButtonUpdate(get_lang('Edit')); + $form->addHidden('id', $embed->getId()); + $form->addHidden('action', 'edit'); + + if ($form->validate()) { + $values = $form->exportValues(); + + $startDate = api_get_utc_datetime($values['range_start'], false, true); + $endDate = api_get_utc_datetime($values['range_end'], false, true); + + $embed + ->setTitle($values['title']) + ->setDisplayStartDate($startDate) + ->setDisplayEndDate($endDate) + ->setHtmlCode($values['html_code']); + + $em->persist($embed); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('Updated'), 'success') + ); + + header('Location: '.api_get_self()); + exit; + } + + $form->setDefaults( + [ + 'title' => $embed->getTitle(), + 'range' => api_get_local_time($embed->getDisplayStartDate()) + .' / ' + .api_get_local_time($embed->getDisplayEndDate()), + 'html_code' => $embed->getHtmlCode(), + ] + ); + + $view->assign('header', $plugin->get_lang('EditEmbeddable')); + $view->assign('form', $form->returnForm()); + break; + case 'delete': + $embedId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; + + if (!$embedId) { + break; + } + + /** @var Embed|null $embed */ + $embed = $embedRepo->find($embedId); + + if (!$embed) { + Display::addFlash(Display::return_message($plugin->get_lang('ContentNotFound'), 'danger')); + + break; + } + + $em->remove($embed); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('Deleted'), 'success') + ); + + header('Location: '.api_get_self()); + exit; + default: + $currentEmbed = $plugin->getCurrentEmbed($course, $session); + + /** @var array|Embed[] $embeds */ + $embeds = $embedRepo->findBy(['course' => $course, 'session' => $session]); + + $tableData = []; + + foreach ($embeds as $embed) { + $data = [ + $embed->getTitle(), + api_convert_and_format_date($embed->getDisplayStartDate()), + api_convert_and_format_date($embed->getDisplayEndDate()), + $embed, + ]; + + if ($isAllowedToEdit) { + $data[] = $embed; + } + + $tableData[] = $data; + } + + if ($isAllowedToEdit) { + $btnAdd = Display::toolbarButton( + $plugin->get_lang('CreateEmbeddable'), + api_get_self().'?action=add', + 'file-code-o', + 'primary' + ); + + $view->assign( + 'actions', + Display::toolbarAction($plugin->get_name(), [$btnAdd]) + ); + + if (in_array($action, ['add', 'edit'])) { + $view->assign('form', $form->returnForm()); + } + } + + if ($currentEmbed) { + $view->assign('current_embed', $currentEmbed); + $view->assign( + 'current_link', + Display::toolbarButton( + $plugin->get_lang('LaunchContent'), + $plugin->getViewUrl($embed), + 'rocket', + 'info' + ) + ); + } + + $table = new SortableTableFromArray($tableData, 1); + $table->set_header(0, get_lang('Title')); + $table->set_header(1, get_lang('AvailableFrom'), true, 'th-header text-center', ['class' => 'text-center']); + $table->set_header(2, get_lang('AvailableTill'), true, 'th-header text-center', ['class' => 'text-center']); + + if ($isAllowedToEdit) { + $table->set_header(3, get_lang('Members'), false, 'th-header text-right', ['class' => 'text-right']); + $table->set_column_filter( + 3, + function (Embed $value) use ($plugin) { + return $plugin->getMembersCount($value); + } + ); + } + + $table->set_header( + $isAllowedToEdit ? 4 : 3, + get_lang('Actions'), + false, + 'th-header text-right', + ['class' => 'text-right'] + ); + $table->set_column_filter( + $isAllowedToEdit ? 4 : 3, + function (Embed $value) use ($isAllowedToEdit, $plugin) { + $actions = []; + + $actions[] = Display::url( + Display::return_icon('external_link.png', get_lang('View')), + $plugin->getViewUrl($value) + ); + + if ($isAllowedToEdit) { + $actions[] = Display::url( + Display::return_icon('edit.png', get_lang('Edit')), + api_get_self().'?action=edit&id='.$value->getId() + ); + + $actions[] = Display::url( + Display::return_icon('delete.png', get_lang('Delete')), + api_get_self().'?action=delete&id='.$value->getId() + ); + } + + return implode(PHP_EOL, $actions); + } + ); + + $view->assign('embeds', $embeds); + $view->assign('table', $table->return_table()); +} + +$content = $view->fetch('embedregistry/view/start.tpl'); + +if ($actions) { + $actions = implode(PHP_EOL, $actions); + + $view->assign( + 'actions', + Display::toolbarAction($plugin->get_name(), [$actions]) + ); +} + +$view->assign('content', $content); +$view->display_one_col_template(); diff --git a/plugin/embedregistry/uninstall.php b/plugin/embedregistry/uninstall.php new file mode 100644 index 000000000..5906ba3cc --- /dev/null +++ b/plugin/embedregistry/uninstall.php @@ -0,0 +1,4 @@ +uninstall(); diff --git a/plugin/embedregistry/view.php b/plugin/embedregistry/view.php new file mode 100644 index 000000000..1b4ca43d3 --- /dev/null +++ b/plugin/embedregistry/view.php @@ -0,0 +1,72 @@ +get(EmbedRegistryPlugin::SETTING_ENABLED)) { + api_not_allowed(true); +} + +$isAllowedToEdit = api_is_allowed_to_edit(true); + +$em = Database::getManager(); +$embedRepo = $em->getRepository('ChamiloPluginBundle:EmbedRegistry\Embed'); + +$course = api_get_course_entity(api_get_course_int_id()); +$session = api_get_session_entity(api_get_session_id()); + +$embedId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; + +if (!$embedId) { + api_not_allowed(true); +} + +/** @var Embed|null $embed */ +$embed = $embedRepo->find($embedId); + +if (!$embed) { + api_not_allowed( + true, + Display::return_message($plugin->get_lang('ContentNotFound'), 'danger') + ); +} + +if ($course->getId() !== $embed->getCourse()->getId()) { + api_not_allowed(true); +} + +if ($session && $embed->getSession()) { + if ($session->getId() !== $embed->getSession()->getId()) { + api_not_allowed(true); + } +} + +$plugin->saveEventAccessTool(); + +$interbreadcrumb[] = [ + 'name' => $plugin->getToolTitle(), + 'url' => api_get_path(WEB_PLUGIN_PATH).$plugin->get_name().'/start.php', +]; + +$actions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).$plugin->get_name().'/start.php?'.api_get_cidreq() +); + +$view = new Template($embed->getTitle()); +$view->assign('header', $embed->getTitle()); +$view->assign('actions', Display::toolbarAction($plugin->get_name(), [$actions])); +$view->assign( + 'content', + '

    '.$plugin->formatDisplayDate($embed).'

    ' + .PHP_EOL + .Security::remove_XSS($embed->getHtmlCode(), COURSEMANAGERLOWSECURITY) +); +$view->display_one_col_template(); diff --git a/plugin/embedregistry/view/start.tpl b/plugin/embedregistry/view/start.tpl new file mode 100644 index 000000000..c53f63aa8 --- /dev/null +++ b/plugin/embedregistry/view/start.tpl @@ -0,0 +1,37 @@ +{% if is_allowed_to_edit %} + {% if external_url is defined %} +
    +

    + {{ 'YouNeedCreateContent'|get_plugin_lang('EmbedRegistryPlugin') }} + {{ 'CreateContent'|get_plugin_lang('EmbedRegistryPlugin') }} +

    +
    + {% endif %} + + {% if form is defined %} + {{ form }} + {% endif %} +{% endif %} + +{% if current_embed is defined %} + {% set start_date %} + + {% endset %} + {% set end_date %} + + {% endset %} + +
    +

    {{ current_embed.title }}

    +

    + {{ 'FromDateXToDateY'|get_lang|format(start_date, end_date) }} +

    +

    {{ current_link }}

    +
    +{% endif %} + +{{ table }} diff --git a/plugin/exercise_signature/README.md b/plugin/exercise_signature/README.md new file mode 100644 index 000000000..2664c08e3 --- /dev/null +++ b/plugin/exercise_signature/README.md @@ -0,0 +1,15 @@ +# Exercises signature + +This plugin allows the teacher to check a box in any exercise configuration to request a signature from the user at the end of the exercise attempt. + +A modal window, a bit like a mini-whiteboard, will then appear to students asking them to sign. The signature can be done through the mouse (unpractical), through a touch device (ideal with tablets) or through a touch-sensitive laptop screen. + +Teachers will see the signature when they review the attempt details. + +## Installation + +This plugin creates extra fields for exercises automatically. + +Signatures are saved as a drawing in the database. + +Uninstalling this plugin will remove the extra fields, so all signatures will disappear (instantly) if you uninstall this. Consider using the "disable" option inside the plugin configuration instead. \ No newline at end of file diff --git a/plugin/exercise_signature/index.php b/plugin/exercise_signature/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/exercise_signature/index.php @@ -0,0 +1 @@ +install(); diff --git a/plugin/exercise_signature/lang/english.php b/plugin/exercise_signature/lang/english.php new file mode 100644 index 000000000..5dc0bb89c --- /dev/null +++ b/plugin/exercise_signature/lang/english.php @@ -0,0 +1,12 @@ + 'boolean', + ] + ); + $this->isAdminPlugin = true; + } + + /** + * @return $this + */ + public static function create() + { + static $instance = null; + + return $instance ? $instance : $instance = new self(); + } + + public static function exerciseHasSignatureActivated(Exercise $exercise) + { + if (empty($exercise->iid)) { + return false; + } + + if ('true' === api_get_plugin_setting('exercise_signature', 'tool_enable')) { + $extraFieldValue = new ExtraFieldValue('exercise'); + $result = $extraFieldValue->get_values_by_handler_and_field_variable($exercise->iid, 'signature_activated'); + if ($result && isset($result['value']) && 1 === (int) $result['value']) { + return true; + } + } + + return false; + } + + public static function saveSignature($userId, $trackInfo, $file) + { + if (false === self::validateSignatureAccess($userId, $trackInfo)) { + return false; + } + + $signature = self::getSignature($userId, $trackInfo); + if (false !== $signature) { + return false; + } + + if (empty($file)) { + return false; + } + + $params = [ + 'item_id' => $trackInfo['exe_id'], + 'extra_signature' => $file, + ]; + $extraFieldValue = new ExtraFieldValue('track_exercise'); + $extraFieldValue->saveFieldValues( + $params, + true + ); + + $signature = self::getSignature($userId, $trackInfo); + if (false !== $signature) { + return true; + } + + return true; + } + + public static function validateSignatureAccess($userId, $trackInfo) + { + $userId = (int) $userId; + if (isset($trackInfo['exe_id']) && isset($trackInfo['exe_user_id']) && + !empty($trackInfo['exe_id']) && !empty($trackInfo['exe_user_id']) && + $trackInfo['status'] !== 'incomplete' + ) { + if ($userId === (int) $trackInfo['exe_user_id']) { + return true; + } + } + + return false; + } + + public static function getSignature($userId, $trackInfo) + { + if (false === self::validateSignatureAccess($userId, $trackInfo)) { + return false; + } + + $extraFieldValue = new ExtraFieldValue('track_exercise'); + $result = $extraFieldValue->get_values_by_handler_and_field_variable($trackInfo['exe_id'], 'signature'); + + if ($result && isset($result['value']) && !empty($result['value'])) { + return $result['value']; + } + + return false; + } + + /** + * Get the plugin Name. + * + * @return string + */ + public function get_name() + { + return 'exercise_signature'; + } + + /** + * Creates this plugin's related tables in the internal database. + * Installs course fields in all courses. + */ + public function install() + { + $extraField = new ExtraField('exercise'); + $extraFieldHandler = $extraField->get_handler_field_info_by_field_variable('signature_activated'); + $exists = $extraFieldHandler !== false; + + if (!$exists) { + $extraField->save( + [ + 'field_type' => 13, // checkbox yes/no + 'variable' => 'signature_activated', + 'display_text' => get_plugin_lang('SignatureActivated', 'ExerciseSignaturePlugin'), + 'default_value' => null, + 'field_order' => null, + 'visible_to_self' => 1, + 'changeable' => 1, + 'filter' => null, + ] + ); + } + + $extraFieldHandler = $extraField->get_handler_field_info_by_field_variable('signature_mandatory'); + $exists = $extraFieldHandler !== false; + + if (!$exists) { + $extraField->save( + [ + 'field_type' => 13, // checkbox yes/no + 'variable' => 'signature_mandatory', + 'display_text' => get_plugin_lang('SignatureMandatory', 'ExerciseSignaturePlugin'), + 'default_value' => null, + 'field_order' => null, + 'visible_to_self' => 1, + 'changeable' => 1, + 'filter' => null, + ] + ); + } + + $extraField = new ExtraField('track_exercise'); + $extraFieldHandler = $extraField->get_handler_field_info_by_field_variable('signature'); + $exists = $extraFieldHandler !== false; + + if (!$exists) { + $extraField->save( + [ + 'field_type' => 2, // textarea + 'variable' => 'signature', + 'display_text' => get_plugin_lang('Signature', 'ExerciseSignaturePlugin'), + 'default_value' => null, + 'field_order' => null, + 'visible_to_self' => 1, + 'changeable' => 1, + 'filter' => null, + ] + ); + } + + $table = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + $sql = "ALTER TABLE $table MODIFY COLUMN value LONGTEXT null;"; + Database::query($sql); + } + + /** + * Drops this plugins' related tables from the internal database. + * Uninstalls course fields in all courses(). + */ + public function uninstall() + { + $extraField = new ExtraField('track_exercise'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('signature_activated'); + + if ($fieldInfo) { + $extraField->delete($fieldInfo['id']); + } + + $extraField = new ExtraField('exercise'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('signature'); + if ($fieldInfo) { + $extraField->delete($fieldInfo['id']); + } + } +} diff --git a/plugin/exercise_signature/plugin.php b/plugin/exercise_signature/plugin.php new file mode 100644 index 000000000..67dec73fe --- /dev/null +++ b/plugin/exercise_signature/plugin.php @@ -0,0 +1,5 @@ +get_info(); diff --git a/plugin/exercise_signature/uninstall.php b/plugin/exercise_signature/uninstall.php new file mode 100644 index 000000000..393069c52 --- /dev/null +++ b/plugin/exercise_signature/uninstall.php @@ -0,0 +1,9 @@ +uninstall(); diff --git a/plugin/exercisefocused/admin.php b/plugin/exercisefocused/admin.php new file mode 100644 index 000000000..e2078618b --- /dev/null +++ b/plugin/exercisefocused/admin.php @@ -0,0 +1,32 @@ +getRepository(Log::class); + +$reportingController = new AdminController( + ExerciseFocusedPlugin::create(), + HttpRequest::createFromGlobals(), + $em, + $logRepository +); + +try { + $response = $reportingController(); +} catch (Exception $e) { + $response = HttpResponse::create('', HttpResponse::HTTP_FORBIDDEN); +} + +$response->send(); diff --git a/plugin/exercisefocused/index.php b/plugin/exercisefocused/index.php new file mode 100644 index 000000000..cfe8e1c38 --- /dev/null +++ b/plugin/exercisefocused/index.php @@ -0,0 +1,48 @@ +isEnableForExercise($exerciseId); + +if ($renderRegion) { + $_template['show_region'] = true; + + $em = Database::getManager(); + + $existingExeId = (int) ChamiloSession::read('exe_id'); + $trackingExercise = null; + + if ($existingExeId) { + $trackingExercise = $em->find(TrackEExercises::class, $existingExeId); + } + + $_template['sec_token'] = Security::get_token('exercisefocused'); + + if ('true' === $plugin->get(ExerciseFocusedPlugin::SETTING_ENABLE_OUTFOCUSED_LIMIT)) { + $logRepository = $em->getRepository(Log::class); + + if ($trackingExercise) { + $countOutfocused = $logRepository->countByActionInExe($trackingExercise, Log::TYPE_OUTFOCUSED); + } else { + $countOutfocused = 0; + } + + $_template['count_outfocused'] = $countOutfocused; + $_template['remaining_outfocused'] = (int) $plugin->get(ExerciseFocusedPlugin::SETTING_OUTFOCUSED_LIMIT) - $countOutfocused; + } + + if ($trackingExercise) { + $exercise = new Exercise($trackingExercise->getCId()); + + if ($exercise->read($trackingExercise->getExeExoId())) { + $_template['exercise_type'] = (int) $exercise->selectType(); + } + } +} diff --git a/plugin/exercisefocused/install.php b/plugin/exercisefocused/install.php new file mode 100644 index 000000000..f66dfb8ec --- /dev/null +++ b/plugin/exercisefocused/install.php @@ -0,0 +1,5 @@ +install(); diff --git a/plugin/exercisefocused/lang/english.php b/plugin/exercisefocused/lang/english.php new file mode 100644 index 000000000..96433f165 --- /dev/null +++ b/plugin/exercisefocused/lang/english.php @@ -0,0 +1,39 @@ +
    You must return and complete it."; +$strings['YouHaveXTimeToReturn'] = "You have %s seconds to return"; +$strings['YouAreAllowedXOutfocused'] = "You are allowed %d outfocused"; +$strings['OutfocusedLimitExceeded'] = "You have exceeded the allowed limit of outfocused"; +$strings['SelectExercise'] = "Select exercise"; +$strings['UnselectExercise'] = "Unselect exercise"; +$strings['Returns'] = "Returns"; +$strings['MaxOutfocusedReached'] = "Max outfocused reached"; +$strings['TimeLimitReached'] = "Time limit reached"; +$strings['Outfocused'] = "Outfocused"; +$strings['Return'] = "Return"; +$strings['Motive'] = "Motive"; +$strings['AlertBeforeLeaving'] = "Please stay within the exam"; +$strings['RandomSampling'] = "Random sampling"; +$strings['WindowTitleOutfocused'] = '🚨 Stay within the exam!'; +$strings['LevelReached'] = 'Level reached'; +$strings['ExerciseStartDateAndTime'] = "Exercise start date and time"; +$strings['ExerciseEndDateAndTime'] = "Exercise end date and time"; +$strings['MotiveExerciseFinished'] = "Successfully completed the exam"; diff --git a/plugin/exercisefocused/lang/spanish.php b/plugin/exercisefocused/lang/spanish.php new file mode 100644 index 000000000..d2348a664 --- /dev/null +++ b/plugin/exercisefocused/lang/spanish.php @@ -0,0 +1,39 @@ +
    Debes retornar y culminarlo."; +$strings['YouHaveXTimeToReturn'] = "Tienes %s segundos para regresar"; +$strings['YouAreAllowedXOutfocused'] = "Se te permite %d desenfoques"; +$strings['OutfocusedLimitExceeded'] = "Has excedido el límite permitido de desenfoques"; +$strings['SelectExercise'] = "Seleccionar ejercicio"; +$strings['UnselectExercise'] = "Deseleccionar ejercicio"; +$strings['Returns'] = "Regresos"; +$strings['MaxOutfocusedReached'] = "Se ha alcanzado el máximo de desenfoques"; +$strings['TimeLimitReached'] = "Se ha alcanzado el límite de tiempo"; +$strings['Outfocused'] = "Desenfoques"; +$strings['Return'] = "Regresos"; +$strings['Motive'] = "Motivo"; +$strings['AlertBeforeLeaving'] = "Por favor, mantente dentro del examen."; +$strings['RandomSampling'] = "Muestreo Aleatorio"; +$strings['WindowTitleOutfocused'] = '🚨 Retorna y culmina tu examen'; +$strings['LevelReached'] = 'Nivel alcanzado'; +$strings['ExerciseStartDateAndTime'] = "Fecha y hora de inicio del ejercicio"; +$strings['ExerciseEndDateAndTime'] = "Fecha y hora de finalización del ejercicio"; +$strings['MotiveExerciseFinished'] = "Culminó exitosamente el examen"; diff --git a/plugin/exercisefocused/pages/detail.php b/plugin/exercisefocused/pages/detail.php new file mode 100644 index 000000000..b258392b7 --- /dev/null +++ b/plugin/exercisefocused/pages/detail.php @@ -0,0 +1,32 @@ +getRepository(Log::class); + +$detailController = new DetailController( + ExerciseFocusedPlugin::create(), + HttpRequest::createFromGlobals(), + $em, + $logRepository +); + +try { + $response = $detailController(); +} catch (Exception $e) { + $response = HttpResponse::create('', HttpResponse::HTTP_FORBIDDEN); +} + +$response->send(); diff --git a/plugin/exercisefocused/pages/export.php b/plugin/exercisefocused/pages/export.php new file mode 100644 index 000000000..2b0b27144 --- /dev/null +++ b/plugin/exercisefocused/pages/export.php @@ -0,0 +1,298 @@ +isEnabled(true); +$request = HttpRequest::createFromGlobals(); +$em = Database::getManager(); +$focusedLogRepository = $em->getRepository(FocusedLog::class); +$attempsRepository = $em->getRepository(TrackEAttempt::class); + +if (!$plugin->isEnabled(true)) { + api_not_allowed(true); +} + +$params = $request->query->all(); + +$results = findResults($params, $em, $plugin); + +$data = []; + +/** @var array $result */ +foreach ($results as $result) { + /** @var TrackEExercises $trackExe */ + $trackExe = $result['exe']; + $user = api_get_user_entity($trackExe->getExeUserId()); + + $outfocusedLimitCount = $focusedLogRepository->countByActionInExe($trackExe, FocusedLog::TYPE_OUTFOCUSED_LIMIT); + $timeLimitCount = $focusedLogRepository->countByActionInExe($trackExe, FocusedLog::TYPE_TIME_LIMIT); + + $exercise = new Exercise($trackExe->getCId()); + $exercise->read($trackExe->getExeExoId()); + + $quizType = (int) $exercise->selectType(); + + $data[] = [ + get_lang('LoginName'), + $user->getUsername(), + ]; + $data[] = [ + get_lang('Student'), + $user->getFirstname(), + $user->getLastname(), + ]; + + if ($monitoringPluginIsEnabled + && 'true' === $monitoringPlugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTION_AGE_DISTINCTION_ENABLE) + ) { + $fieldVariable = $monitoringPlugin->get(ExerciseMonitoringPlugin::SETTING_EXTRAFIELD_BIRTHDATE); + $birthdateValue = UserManager::get_extra_user_data_by_field($user->getId(), $fieldVariable); + + $data[] = [ + $monitoringPlugin->get_lang('Birthdate'), + $birthdateValue ? $birthdateValue[$fieldVariable] : '----', + $monitoringPlugin->isAdult($user->getId()) + ? $monitoringPlugin->get_lang('AdultStudent') + : $monitoringPlugin->get_lang('MinorStudent'), + ]; + } + + if ($trackExe->getSessionId()) { + $data[] = [ + get_lang('SessionName'), + api_get_session_entity($trackExe->getSessionId())->getName(), + ]; + } + + $data[] = [ + get_lang('CourseTitle'), + api_get_course_entity($trackExe->getCId())->getTitle(), + ]; + $data[] = [ + get_lang('ExerciseName'), + $exercise->getUnformattedTitle(), + ]; + $data[] = [ + $plugin->get_lang('ExerciseStartDateAndTime'), + api_get_local_time($result['exe']->getStartDate(), null, null, true, true, true), + ]; + $data[] = [ + $plugin->get_lang('ExerciseEndDateAndTime'), + api_get_local_time($result['exe']->getExeDate(), null, null, true, true, true), + ]; + $data[] = [ + get_lang('IP'), + $result['exe']->getUserIp(), + ]; + $data[] = [ + $plugin->get_lang('Motive'), + $plugin->calculateMotive($outfocusedLimitCount, $timeLimitCount), + ]; + $data[] = []; + + $data[] = [ + $plugin->get_lang('LevelReached'), + get_lang('DateExo'), + get_lang('Score'), + $plugin->get_lang('Outfocused'), + $plugin->get_lang('Returns'), + $monitoringPluginIsEnabled ? $monitoringPlugin->get_lang('Snapshots') : '', + ]; + + if (ONE_PER_PAGE === $quizType) { + $questionList = explode(',', $trackExe->getDataTracking()); + + foreach ($questionList as $idx => $questionId) { + $attempt = $attempsRepository->findOneBy( + ['exeId' => $trackExe->getExeId(), 'questionId' => $questionId], + ['tms' => 'DESC'] + ); + + if (!$attempt) { + continue; + } + + $result = $exercise->manage_answer( + $trackExe->getExeId(), + $questionId, + null, + 'exercise_result', + false, + false, + true, + false, + $exercise->selectPropagateNeg() + ); + + $row = [ + get_lang('QuestionNumber').' '.($idx + 1), + api_get_local_time($attempt->getTms()), + $result['score'].' / '.$result['weight'], + $focusedLogRepository->countByActionAndLevel($trackExe, FocusedLog::TYPE_OUTFOCUSED, $questionId), + $focusedLogRepository->countByActionAndLevel($trackExe, FocusedLog::TYPE_RETURN, $questionId), + getSnapshotListForLevel($questionId, $trackExe), + ]; + + $data[] = $row; + } + } elseif (ALL_ON_ONE_PAGE === $quizType) { + } + + $data[] = []; + $data[] = []; + $data[] = []; +} + +Export::arrayToXls($data); + +function getSessionIdFromFormValues(array $formValues, array $fieldVariableList): array +{ + $fieldItemIdList = []; + $objFieldValue = new ExtraFieldValue('session'); + + foreach ($fieldVariableList as $fieldVariable) { + if (!isset($formValues["extra_$fieldVariable"])) { + continue; + } + + $itemValues = $objFieldValue->get_item_id_from_field_variable_and_field_value( + $fieldVariable, + $formValues["extra_$fieldVariable"], + false, + false, + true + ); + + foreach ($itemValues as $itemValue) { + $fieldItemIdList[] = (int) $itemValue['item_id']; + } + } + + return array_unique($fieldItemIdList); +} + +function findResults(array $formValues, EntityManagerInterface $em, ExerciseFocusedPlugin $plugin) +{ + $cId = api_get_course_int_id(); + $sId = api_get_session_id(); + + $qb = $em->createQueryBuilder(); + $qb + ->select('te AS exe, q.title, te.startDate , u.firstname, u.lastname, u.username') + ->from(TrackEExercises::class, 'te') + ->innerJoin(CQuiz::class, 'q', Join::WITH, 'te.exeExoId = q.iid') + ->innerJoin(User::class, 'u', Join::WITH, 'te.exeUserId = u.id'); + + $params = []; + + if ($cId) { + $qb->andWhere($qb->expr()->eq('te.cId', ':cId')); + + $params['cId'] = $cId; + + $sessionItemIdList = $sId ? [$sId] : []; + } else { + $sessionItemIdList = getSessionIdFromFormValues( + $formValues, + $plugin->getSessionFieldList() + ); + } + + if ($sessionItemIdList) { + $qb->andWhere($qb->expr()->in('te.sessionId', ':sessionItemIdList')); + + $params['sessionItemIdList'] = $sessionItemIdList; + } + + if (!empty($formValues['username'])) { + $qb->andWhere($qb->expr()->eq('u.username', ':username')); + + $params['username'] = $formValues['username']; + } + + if (!empty($formValues['firstname'])) { + $qb->andWhere($qb->expr()->eq('u.firstname', ':firstname')); + + $params['firstname'] = $formValues['firstname']; + } + + if (!empty($formValues['lastname'])) { + $qb->andWhere($qb->expr()->eq('u.lastname', ':lastname')); + + $params['lastname'] = $formValues['lastname']; + } + + if (!empty($formValues['start_date'])) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->gte('te.startDate', ':start_date'), + $qb->expr()->lte('te.exeDate', ':end_date') + ) + ); + + $params['start_date'] = api_get_utc_datetime($formValues['start_date'].' 00:00:00', false, true); + $params['end_date'] = api_get_utc_datetime($formValues['start_date'].' 23:59:59', false, true); + } + + if (empty($params)) { + return []; + } + + if ($cId && !empty($formValues['id'])) { + $qb->andWhere($qb->expr()->eq('q.iid', ':q_id')); + + $params['q_id'] = $formValues['id']; + } + + $qb->setParameters($params); + + $query = $qb->getQuery(); + + return $query->getResult(); +} + +function getSnapshotListForLevel(int $level, TrackEExercises $trackExe): string +{ + $monitoringPluginIsEnabled = ExerciseMonitoringPlugin::create()->isEnabled(true); + + if (!$monitoringPluginIsEnabled) { + return ''; + } + + $user = api_get_user_entity($trackExe->getExeUserId()); + $monitoringLogRepository = Database::getManager()->getRepository(MonitoringLog::class); + + $monitoringLogsByQuestion = $monitoringLogRepository->findByLevelAndExe($level, $trackExe); + $snapshotList = []; + + /** @var MonitoringLog $logByQuestion */ + foreach ($monitoringLogsByQuestion as $logByQuestion) { + $snapshotUrl = ExerciseMonitoringPlugin::generateSnapshotUrl( + $user->getId(), + $logByQuestion->getImageFilename() + ); + $snapshotList[] = api_get_local_time($logByQuestion->getCreatedAt()).' '.$snapshotUrl; + } + + return implode(PHP_EOL, $snapshotList); +} diff --git a/plugin/exercisefocused/pages/log.php b/plugin/exercisefocused/pages/log.php new file mode 100644 index 000000000..b6b98e94a --- /dev/null +++ b/plugin/exercisefocused/pages/log.php @@ -0,0 +1,30 @@ +getRepository(Log::class); + +$logController = new LogController( + ExerciseFocusedPlugin::create(), + HttpRequest::createFromGlobals(), + $em, + $logRepository +); + +try { + $response = $logController(); +} catch (Exception $e) { + $response = HttpResponse::create('', HttpResponse::HTTP_FORBIDDEN); +} + +$response->send(); diff --git a/plugin/exercisefocused/pages/reporting.php b/plugin/exercisefocused/pages/reporting.php new file mode 100644 index 000000000..06c962518 --- /dev/null +++ b/plugin/exercisefocused/pages/reporting.php @@ -0,0 +1,34 @@ +getRepository(Log::class); + +$startController = new ReportingController( + ExerciseFocusedPlugin::create(), + HttpRequest::createFromGlobals(), + $em, + $logRepository +); + +//try { + $response = $startController(); +//} catch (Exception $e) { + //$response = HttpResponse::create('', HttpResponse::HTTP_FORBIDDEN); +//} + +$response->send(); diff --git a/plugin/exercisefocused/plugin.php b/plugin/exercisefocused/plugin.php new file mode 100644 index 000000000..905029d99 --- /dev/null +++ b/plugin/exercisefocused/plugin.php @@ -0,0 +1,10 @@ +get_info(); + +$plugin_info['templates'] = [ + 'templates/script.html.twig', + 'templates/block.html.twig', +]; diff --git a/plugin/exercisefocused/src/Controller/AdminController.php b/plugin/exercisefocused/src/Controller/AdminController.php new file mode 100644 index 000000000..c28d3ec18 --- /dev/null +++ b/plugin/exercisefocused/src/Controller/AdminController.php @@ -0,0 +1,52 @@ +createForm(); + + $results = []; + + if ($form->validate()) { + $results = $this->findResults( + $form->exportValues() + ); + } + + $table = $this->createTable($results); + + $content = $form->returnForm() + .Display::page_subheader2($this->plugin->get_lang('ReportByAttempts')) + .$table->toHtml(); + + $this->setBreadcrumb(); + + return $this->renderView( + $this->plugin->get_title(), + $content + ); + } + + private function setBreadcrumb() + { + $codePath = api_get_path(WEB_CODE_PATH); + + $GLOBALS['interbreadcrumb'][] = [ + 'url' => $codePath.'admin/index.php', + 'name' => get_lang('Administration'), + ]; + } +} diff --git a/plugin/exercisefocused/src/Controller/BaseController.php b/plugin/exercisefocused/src/Controller/BaseController.php new file mode 100644 index 000000000..61e26e9ff --- /dev/null +++ b/plugin/exercisefocused/src/Controller/BaseController.php @@ -0,0 +1,86 @@ +plugin = $plugin; + $this->request = $request; + $this->em = $em; + $this->logRepository = $logRepository; + } + + /** + * @throws Exception + */ + public function __invoke(): HttpResponse + { + if (!$this->plugin->isEnabled(true)) { + throw new Exception(); + } + + return HttpResponse::create(); + } + + protected function renderView( + string $title, + string $content, + ?string $header = null, + array $actions = [] + ): HttpResponse { + if (!$header) { + $header = $title; + } + + $this->template = new Template($title); + $this->template->assign('header', $header); + $this->template->assign('actions', implode(PHP_EOL, $actions)); + $this->template->assign('content', $content); + + ob_start(); + $this->template->display_one_col_template(); + $html = ob_get_contents(); + ob_end_clean(); + + return HttpResponse::create($html); + } +} diff --git a/plugin/exercisefocused/src/Controller/DetailController.php b/plugin/exercisefocused/src/Controller/DetailController.php new file mode 100644 index 000000000..9d2f2c60e --- /dev/null +++ b/plugin/exercisefocused/src/Controller/DetailController.php @@ -0,0 +1,98 @@ +request->query->getInt('id'); + $exe = $this->em->find(TrackEExercises::class, $exeId); + + if (!$exe) { + throw new Exception(); + } + + $user = api_get_user_entity($exe->getExeUserId()); + + $objExercise = new Exercise($exe->getCId()); + $objExercise->read($exe->getExeExoId()); + + $logs = $this->logRepository->findBy(['exe' => $exe], ['updatedAt' => 'ASC']); + $table = $this->getTable($objExercise, $logs); + + $content = $this->generateHeader($objExercise, $user, $exe) + .'
    ' + .$table->toHtml(); + + return HttpResponse::create($content); + } + + /** + * @param array $logs + * + * @return void + */ + private function getTable(Exercise $objExercise, array $logs): HTML_Table + { + $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']); + $table->setHeaderContents(0, 0, get_lang('Action')); + $table->setHeaderContents(0, 1, get_lang('DateTime')); + $table->setHeaderContents(0, 2, $this->plugin->get_lang('LevelReached')); + + $row = 1; + + foreach ($logs as $log) { + $strLevel = ''; + + if (ONE_PER_PAGE == $objExercise->selectType()) { + try { + $question = $this->em->find(CQuizQuestion::class, $log->getLevel()); + + $strLevel = $question->getQuestion(); + } catch (Exception $exception) { + } + } + + $table->setCellContents( + $row, + 0, + $this->plugin->getActionTitle($log->getAction()) + ); + $table->setCellContents( + $row, + 1, + api_get_local_time($log->getCreatedAt(), null, null, true, true, true) + ); + $table->setCellContents($row, 2, $strLevel); + + $row++; + } + + return $table; + } +} diff --git a/plugin/exercisefocused/src/Controller/LogController.php b/plugin/exercisefocused/src/Controller/LogController.php new file mode 100644 index 000000000..380c276ef --- /dev/null +++ b/plugin/exercisefocused/src/Controller/LogController.php @@ -0,0 +1,97 @@ +request->query->get('action'); + $levelId = $this->request->query->getInt('level_id'); + + $exeId = (int) ChamiloSession::read('exe_id'); + + if (!in_array($action, self::VALID_ACTIONS)) { + throw new Exception('action invalid'); + } + + $trackingExercise = $this->em->find(TrackEExercises::class, $exeId); + + if (!$trackingExercise) { + throw new Exception('no exercise attempt'); + } + + $objExercise = new Exercise($trackingExercise->getCId()); + $objExercise->read($trackingExercise->getExeExoId()); + + $level = 0; + + if (ONE_PER_PAGE == $objExercise->selectType()) { + $question = $this->em->find(CQuizQuestion::class, $levelId); + + if (!$question) { + throw new Exception('Invalid level'); + } + + $level = $question->getIid(); + } + + $log = new Log(); + $log + ->setAction($action) + ->setExe($trackingExercise) + ->setLevel($level); + + $this->em->persist($log); + $this->em->flush(); + + $remainingOutfocused = -1; + + if ('true' === $this->plugin->get(ExerciseFocusedPlugin::SETTING_ENABLE_OUTFOCUSED_LIMIT)) { + $countOutfocused = $this->logRepository->countByActionInExe($trackingExercise, Log::TYPE_OUTFOCUSED); + + $remainingOutfocused = (int) $this->plugin->get(ExerciseFocusedPlugin::SETTING_OUTFOCUSED_LIMIT) - $countOutfocused; + } + + $exercise = new Exercise(api_get_course_int_id()); + $exercise->read($trackingExercise->getExeExoId()); + + $json = [ + 'sec_token' => Security::get_token('exercisefocused'), + 'remainingOutfocused' => $remainingOutfocused, + ]; + + return JsonResponse::create($json); + } +} diff --git a/plugin/exercisefocused/src/Controller/ReportingController.php b/plugin/exercisefocused/src/Controller/ReportingController.php new file mode 100644 index 000000000..1c29ad1cd --- /dev/null +++ b/plugin/exercisefocused/src/Controller/ReportingController.php @@ -0,0 +1,138 @@ +em->find( + CQuiz::class, + $this->request->query->getInt('id') + ); + + if (!$exercise) { + throw new Exception(); + } + + $courseCode = api_get_course_id(); + $sessionId = api_get_session_id(); + + $tab1 = $this->generateTabResume($exercise); + + $tab2 = $this->generateTabSearch($exercise, $courseCode, $sessionId); + + $tab3 = $this->generateTabSampling($exercise); + + $content = Display::tabs( + [ + $this->plugin->get_lang('ReportByAttempts'), + get_lang('Search'), + $this->plugin->get_lang('RandomSampling'), + ], + [$tab1, $tab2, $tab3], + 'exercise-focused-tabs', + [], + [], + isset($_GET['submit']) ? 2 : 1 + ); + + $this->setBreadcrumb($exercise->getId()); + + return $this->renderView( + $this->plugin->get_lang('ReportByAttempts'), + $content, + $exercise->getTitle() + ); + } + + private function generateTabResume(CQuiz $exercise): string + { + $results = $this->findResultsInCourse($exercise->getId()); + + return $this->createTable($results)->toHtml(); + } + + /** + * @throws Exception + */ + private function generateTabSearch(CQuiz $exercise, string $courseCode, int $sessionId): string + { + $form = $this->createForm(); + $form->updateAttributes(['action' => api_get_self().'?'.api_get_cidreq().'&id='.$exercise->getId()]); + $form->addHidden('cidReq', $courseCode); + $form->addHidden('id_session', $sessionId); + $form->addHidden('gidReq', 0); + $form->addHidden('gradebook', 0); + $form->addHidden('origin', api_get_origin()); + $form->addHidden('id', $exercise->getId()); + + $tableHtml = ''; + $actions = ''; + + if ($form->validate()) { + $formValues = $form->exportValues(); + + $actionLeft = Display::url( + Display::return_icon('export_excel.png', get_lang('ExportExcel'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'exercisefocused/pages/export.php?'.http_build_query($formValues) + ); + $actionRight = Display::toolbarButton( + get_lang('Clean'), + api_get_path(WEB_PLUGIN_PATH) + .'exercisefocused/pages/reporting.php?' + .api_get_cidreq().'&'.http_build_query(['id' => $exercise->getId(), 'submit' => '']), + 'search' + ); + + $actions = Display::toolbarAction( + 'em-actions', + [$actionLeft, $actionRight] + ); + + $results = $this->findResults($formValues); + + $tableHtml = $this->createTable($results)->toHtml(); + } + + return $form->returnForm().$actions.$tableHtml; + } + + private function generateTabSampling(CQuiz $exercise): string + { + $results = $this->findRandomResults($exercise->getId()); + + return $this->createTable($results)->toHtml(); + } + + /** + * @return array + */ + private function setBreadcrumb($exerciseId): void + { + $codePath = api_get_path('WEB_CODE_PATH'); + $cidReq = api_get_cidreq(); + + $GLOBALS['interbreadcrumb'][] = [ + 'url' => $codePath."exercise/exercise.php?$cidReq", + 'name' => get_lang('Exercises'), + ]; + $GLOBALS['interbreadcrumb'][] = [ + 'url' => $codePath."exercise/exercise_report.php?$cidReq&".http_build_query(['exerciseId' => $exerciseId]), + 'name' => get_lang('StudentScore'), + ]; + } +} diff --git a/plugin/exercisefocused/src/Entity/Log.php b/plugin/exercisefocused/src/Entity/Log.php new file mode 100644 index 000000000..8fa8ed650 --- /dev/null +++ b/plugin/exercisefocused/src/Entity/Log.php @@ -0,0 +1,99 @@ +id; + } + + public function getExe(): TrackEExercises + { + return $this->exe; + } + + public function setExe(TrackEExercises $exe): Log + { + $this->exe = $exe; + + return $this; + } + + public function getLevel(): int + { + return $this->level; + } + + public function setLevel(int $level): self + { + $this->level = $level; + + return $this; + } + + public function getAction(): string + { + return $this->action; + } + + public function setAction(string $action): Log + { + $this->action = $action; + + return $this; + } +} diff --git a/plugin/exercisefocused/src/ExerciseFocusedPlugin.php b/plugin/exercisefocused/src/ExerciseFocusedPlugin.php new file mode 100644 index 000000000..62feda2d1 --- /dev/null +++ b/plugin/exercisefocused/src/ExerciseFocusedPlugin.php @@ -0,0 +1,213 @@ + 'boolean', + self::SETTING_ENABLE_TIME_LIMIT => 'boolean', + self::SETTING_TIME_LIMIT => 'text', + self::SETTING_ENABLE_OUTFOCUSED_LIMIT => 'boolean', + self::SETTING_OUTFOCUSED_LIMIT => 'text', + self::SETTING_SESSION_FIELD_FILTERS => 'text', + self::SETTING_PERCENTAGE_SAMPLING => 'text', + ]; + + parent::__construct( + "0.0.1", + "Angel Fernando Quiroz Campos ", + $settings + ); + } + + public static function create(): ?ExerciseFocusedPlugin + { + static $result = null; + + return $result ?: $result = new self(); + } + + /** + * @throws ToolsException + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_LOG])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Log::class), + ] + ); + + $objField = new ExtraField('exercise'); + $objField->save([ + 'variable' => self::FIELD_SELECTED, + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + 'display_text' => $this->get_title(), + 'visible_to_self' => true, + 'changeable' => true, + 'filter' => false, + ]); + } + + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_LOG])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Log::class), + ] + ); + + $objField = new ExtraField('exercise'); + $extraFieldInfo = $objField->get_handler_field_info_by_field_variable(self::FIELD_SELECTED); + + if ($extraFieldInfo) { + $objField->delete($extraFieldInfo['id']); + } + } + + public function getAdminUrl(): string + { + $name = $this->get_name(); + $webPath = api_get_path(WEB_PLUGIN_PATH).$name; + + return "$webPath/admin.php"; + } + + public function getActionTitle($action): string + { + switch ($action) { + case Log::TYPE_OUTFOCUSED: + return $this->get_lang('Outfocused'); + case Log::TYPE_RETURN: + return $this->get_lang('Return'); + case Log::TYPE_OUTFOCUSED_LIMIT: + return $this->get_lang('MaxOutfocusedReached'); + case Log::TYPE_TIME_LIMIT: + return $this->get_lang('TimeLimitReached'); + } + + return ''; + } + + public function getLinkReporting(int $exerciseId): string + { + if (!$this->isEnabled(true)) { + return ''; + } + + $values = (new ExtraFieldValue('exercise')) + ->get_values_by_handler_and_field_variable($exerciseId, self::FIELD_SELECTED); + + if (!$values || !$values['value']) { + return ''; + } + + $icon = Display::return_icon( + 'window_list_slide.png', + $this->get_lang('ReportByAttempts'), + [], + ICON_SIZE_MEDIUM + ); + + $url = api_get_path(WEB_PLUGIN_PATH) + .'exercisefocused/pages/reporting.php?' + .api_get_cidreq().'&'.http_build_query(['id' => $exerciseId]); + + return Display::url($icon, $url); + } + + public function getSessionFieldList(): array + { + $settingField = $this->get(self::SETTING_SESSION_FIELD_FILTERS); + + $fields = explode(',', $settingField); + + return array_map('trim', $fields); + } + + public function isEnableForExercise(int $exerciseId): bool + { + $renderRegion = $this->isEnabled(true) + && strpos($_SERVER['SCRIPT_NAME'], '/main/exercise/exercise_submit.php') !== false; + + if (!$renderRegion) { + return false; + } + + $objFieldValue = new ExtraFieldValue('exercise'); + $values = $objFieldValue->get_values_by_handler_and_field_variable( + $exerciseId, + self::FIELD_SELECTED + ); + + return $values && (bool) $values['value']; + } + + public function calculateMotive(int $outfocusedLimitCount, int $timeLimitCount) + { + $motive = $this->get_lang('MotiveExerciseFinished'); + + if ($outfocusedLimitCount > 0) { + $motive = $this->get_lang('MaxOutfocusedReached'); + } + + if ($timeLimitCount > 0) { + $motive = $this->get_lang('TimeLimitReached'); + } + + return $motive; + } + + protected function createLinkToCourseTool($name, $courseId, $iconName = null, $link = null, $sessionId = 0, $category = 'plugin'): ?CTool + { + $tool = parent::createLinkToCourseTool($name, $courseId, $iconName, $link, $sessionId, $category); + + if (!$tool) { + return null; + } + + $tool->setName( + $tool->getName().':teacher' + ); + + $em = Database::getManager(); + $em->persist($tool); + $em->flush(); + + return $tool; + } +} diff --git a/plugin/exercisefocused/src/Repository/LogRepository.php b/plugin/exercisefocused/src/Repository/LogRepository.php new file mode 100644 index 000000000..01fb4d190 --- /dev/null +++ b/plugin/exercisefocused/src/Repository/LogRepository.php @@ -0,0 +1,28 @@ +count([ + 'exe' => $exe, + 'action' => $action, + ]); + } + + public function countByActionAndLevel(TrackEExercises $exe, string $action, int $level): int + { + return $this->count([ + 'exe' => $exe, + 'action' => $action, + 'level' => $level, + ]); + } +} diff --git a/plugin/exercisefocused/src/Traits/DetailControllerTrait.php b/plugin/exercisefocused/src/Traits/DetailControllerTrait.php new file mode 100644 index 000000000..95a5166d9 --- /dev/null +++ b/plugin/exercisefocused/src/Traits/DetailControllerTrait.php @@ -0,0 +1,28 @@ +getStartDate(), null, null, true, true, true); + $endDate = api_get_local_time($trackExe->getExeDate(), null, null, true, true, true); + + return Display::page_subheader2($objExercise->selectTitle()) + .Display::tag('p', $student->getCompleteNameWithUsername(), ['class' => 'lead']) + .Display::tag( + 'p', + sprintf(get_lang('QuizRemindStartDate'), $startDate) + .sprintf(get_lang('QuizRemindEndDate'), $endDate) + .sprintf(get_lang('QuizRemindDuration'), api_format_time($trackExe->getExeDuration())) + ); + } +} diff --git a/plugin/exercisefocused/src/Traits/ReportingFilterTrait.php b/plugin/exercisefocused/src/Traits/ReportingFilterTrait.php new file mode 100644 index 000000000..ad8126d73 --- /dev/null +++ b/plugin/exercisefocused/src/Traits/ReportingFilterTrait.php @@ -0,0 +1,393 @@ +plugin->getSessionFieldList(); + $cId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + + $form = new FormValidator('exercisefocused', 'get'); + $form->addText('username', get_lang('LoginName'), false); + $form->addText('firstname', get_lang('FirstName'), false); + $form->addText('lastname', get_lang('LastName'), false); + + if ($extraFieldNameList && ($sessionId || !$cId)) { + (new ExtraField('session')) + ->addElements( + $form, + $sessionId, + [], + false, + false, + $extraFieldNameList + ); + + $extraNames = []; + + foreach ($extraFieldNameList as $key => $value) { + $extraNames[$key] = "extra_$value"; + } + + if ($sessionId) { + $form->freeze($extraNames); + } + } + + $form->addDatePicker('start_date', get_lang('StartDate')); + $form->addButtonSearch(get_lang('Search')); + //$form->protect(); + + return $form; + } + + /** + * @throws Exception + */ + protected function findResults(array $formValues = []): array + { + $cId = api_get_course_int_id(); + $sId = api_get_session_id(); + + $qb = $this->em->createQueryBuilder(); + $qb + ->select('te AS exe, q.title, te.startDate, u.id AS user_id, u.firstname, u.lastname, u.username, te.sessionId, te.cId') + ->from(TrackEExercises::class, 'te') + ->innerJoin(CQuiz::class, 'q', Join::WITH, 'te.exeExoId = q.iid') + ->innerJoin(User::class, 'u', Join::WITH, 'te.exeUserId = u.id'); + + $params = []; + + if ($cId) { + $qb->andWhere($qb->expr()->eq('te.cId', ':cId')); + + $params['cId'] = $cId; + + $sessionItemIdList = $sId ? [$sId] : []; + } else { + $sessionItemIdList = $this->getSessionIdFromFormValues( + $formValues, + $this->plugin->getSessionFieldList() + ); + } + + if ($sessionItemIdList) { + $qb->andWhere($qb->expr()->in('te.sessionId', ':sessionItemIdList')); + + $params['sessionItemIdList'] = $sessionItemIdList; + } + + if (!empty($formValues['username'])) { + $qb->andWhere($qb->expr()->eq('u.username', ':username')); + + $params['username'] = $formValues['username']; + } + + if (!empty($formValues['firstname'])) { + $qb->andWhere($qb->expr()->like('u.firstname', ':firstname')); + + $params['firstname'] = $formValues['firstname'].'%'; + } + + if (!empty($formValues['lastname'])) { + $qb->andWhere($qb->expr()->like('u.lastname', ':lastname')); + + $params['lastname'] = $formValues['lastname'].'%'; + } + + if (!empty($formValues['start_date'])) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->gte('te.startDate', ':start_date'), + $qb->expr()->lte('te.exeDate', ':end_date') + ) + ); + + $params['start_date'] = api_get_utc_datetime($formValues['start_date'].' 00:00:00', false, true); + $params['end_date'] = api_get_utc_datetime($formValues['start_date'].' 23:59:59', false, true); + } + + if (empty($params)) { + return []; + } + + if ($cId && !empty($formValues['id'])) { + $qb->andWhere($qb->expr()->eq('q.iid', ':q_id')); + + $params['q_id'] = $formValues['id']; + } + + $qb->setParameters($params); + + return $this->formatResults( + $qb->getQuery()->getResult() + ); + } + + protected function formatResults(array $queryResults): array + { + $results = []; + + foreach ($queryResults as $value) { + $outfocusedCount = $this->logRepository->countByActionInExe($value['exe'], Log::TYPE_OUTFOCUSED); + $returnCount = $this->logRepository->countByActionInExe($value['exe'], Log::TYPE_RETURN); + $outfocusedLimitCount = $this->logRepository->countByActionInExe($value['exe'], Log::TYPE_OUTFOCUSED_LIMIT); + $timeLimitCount = $this->logRepository->countByActionInExe($value['exe'], Log::TYPE_TIME_LIMIT); + + $class = 'success'; + $motive = $this->plugin->get_lang('MotiveExerciseFinished'); + + if ($outfocusedCount > 0 || $returnCount > 0) { + $class = 'warning'; + } + + if ($outfocusedLimitCount > 0 || $timeLimitCount > 0) { + $class = 'danger'; + + if ($outfocusedLimitCount > 0) { + $motive = $this->plugin->get_lang('MaxOutfocusedReached'); + } + + if ($timeLimitCount > 0) { + $motive = $this->plugin->get_lang('TimeLimitReached'); + } + } + + $session = api_get_session_entity($value['sessionId']); + $course = api_get_course_entity($value['cId']); + + $results[] = [ + 'id' => $value['exe']->getExeId(), + 'quiz_title' => $value['title'], + 'user_id' => $value['user_id'], + 'username' => $value['username'], + 'firstname' => $value['firstname'], + 'lastname' => $value['lastname'], + 'start_date' => $value['exe']->getStartDate(), + 'end_date' => $value['exe']->getExeDate(), + 'count_outfocused' => $outfocusedCount, + 'count_return' => $returnCount, + 'motive' => Display::span($motive, ['class' => "text-$class"]), + 'class' => $class, + 'session_name' => $session ? $session->getName() : null, + 'course_title' => $course->getTitle(), + ]; + } + + return $results; + } + + protected function createTable(array $resultData): HTML_Table + { + $courseId = api_get_course_int_id(); + + $pluginMonitoring = ExerciseMonitoringPlugin::create(); + $isPluginMonitoringEnabled = $pluginMonitoring->isEnabled(true); + + $detailIcon = Display::return_icon('forum_listview.png', get_lang('Detail')); + + $urlDetail = api_get_path(WEB_PLUGIN_PATH).'exercisefocused/pages/detail.php?'.api_get_cidreq().'&'; + + $tableHeaders = []; + $tableHeaders[] = get_lang('LoginName'); + $tableHeaders[] = get_lang('FirstName'); + $tableHeaders[] = get_lang('LastName'); + + if (!$courseId) { + $tableHeaders[] = get_lang('SessionName'); + $tableHeaders[] = get_lang('CourseTitle'); + $tableHeaders[] = get_lang('ExerciseName'); + } + + $tableHeaders[] = $this->plugin->get_lang('ExerciseStartDateAndTime'); + $tableHeaders[] = $this->plugin->get_lang('ExerciseEndDateAndTime'); + $tableHeaders[] = $this->plugin->get_lang('Outfocused'); + $tableHeaders[] = $this->plugin->get_lang('Returns'); + $tableHeaders[] = $this->plugin->get_lang('Motive'); + $tableHeaders[] = get_lang('Actions'); + + $tableData = []; + + foreach ($resultData as $result) { + $actionLinks = Display::url( + $detailIcon, + $urlDetail.http_build_query(['id' => $result['id']]), + [ + 'class' => 'ajax', + 'data-title' => get_lang('Detail'), + ] + ); + + if ($isPluginMonitoringEnabled) { + $actionLinks .= $pluginMonitoring->generateDetailLink( + (int) $result['id'], + $result['user_id'] + ); + } + + $row = []; + + $row[] = $result['username']; + $row[] = $result['firstname']; + $row[] = $result['lastname']; + + if (!$courseId) { + $row[] = $result['session_name']; + $row[] = $result['course_title']; + $row[] = $result['quiz_title']; + } + + $row[] = api_get_local_time($result['start_date'], null, null, true, true, true); + $row[] = api_get_local_time($result['end_date'], null, null, true, true, true); + $row[] = $result['count_outfocused']; + $row[] = $result['count_return']; + $row[] = $result['motive']; + $row[] = $actionLinks; + + $tableData[] = $row; + } + + $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']); + $table->setHeaders($tableHeaders); + $table->setData($tableData); + $table->setColAttributes($courseId ? 3 : 6, ['class' => 'text-center']); + $table->setColAttributes($courseId ? 4 : 7, ['class' => 'text-center']); + $table->setColAttributes($courseId ? 5 : 8, ['class' => 'text-right']); + $table->setColAttributes($courseId ? 6 : 9, ['class' => 'text-right']); + $table->setColAttributes($courseId ? 7 : 10, ['class' => 'text-center']); + $table->setColAttributes($courseId ? 8 : 11, ['class' => 'text-right']); + + foreach ($resultData as $idx => $result) { + $table->setRowAttributes($idx + 1, ['class' => $result['class']], true); + } + + return $table; + } + + protected function findResultsInCourse(int $exerciseId, bool $randomResults = false): array + { + $exeIdList = $this->getAttemptsIdForExercise($exerciseId); + + if ($randomResults) { + $exeIdList = $this->pickRandomAttempts($exeIdList) ?: $exeIdList; + } + + if (empty($exeIdList)) { + return []; + } + + $qb = $this->em->createQueryBuilder(); + $qb + ->select('te AS exe, q.title, te.startDate, u.id AS user_id, u.firstname, u.lastname, u.username, te.sessionId, te.cId') + ->from(TrackEExercises::class, 'te') + ->innerJoin(CQuiz::class, 'q', Join::WITH, 'te.exeExoId = q.iid') + ->innerJoin(User::class, 'u', Join::WITH, 'te.exeUserId = u.id') + ->andWhere( + $qb->expr()->in('te.exeId', $exeIdList) + ) + ->addOrderBy('te.startDate'); + + return $this->formatResults( + $qb->getQuery()->getResult() + ); + } + + protected function findRandomResults(int $exerciseId): array + { + return $this->findResultsInCourse($exerciseId, true); + } + + private function getSessionIdFromFormValues(array $formValues, array $fieldVariableList): array + { + $fieldItemIdList = []; + $objFieldValue = new ExtraFieldValue('session'); + + foreach ($fieldVariableList as $fieldVariable) { + if (!isset($formValues["extra_$fieldVariable"])) { + continue; + } + + $itemValues = $objFieldValue->get_item_id_from_field_variable_and_field_value( + $fieldVariable, + $formValues["extra_$fieldVariable"], + false, + false, + true + ); + + foreach ($itemValues as $itemValue) { + $fieldItemIdList[] = (int) $itemValue['item_id']; + } + } + + return array_unique($fieldItemIdList); + } + + private function getAttemptsIdForExercise(int $exerciseId): array + { + $cId = api_get_course_int_id(); + $sId = api_get_session_id(); + + $tblTrackExe = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); + + $sessionCondition = api_get_session_condition($sId); + + $result = Database::query( + "SELECT exe_id FROM $tblTrackExe + WHERE c_id = $cId + AND exe_exo_id = $exerciseId + $sessionCondition + ORDER BY exe_id" + ); + + return array_column( + Database::store_result($result), + 'exe_id' + ); + } + + private function pickRandomAttempts(array $attemptIdList): array + { + $settingPercentage = (int) $this->plugin->get(ExerciseFocusedPlugin::SETTING_PERCENTAGE_SAMPLING); + + if (!$settingPercentage) { + return []; + } + + $percentage = count($attemptIdList) * ($settingPercentage / 100); + $round = round($percentage) ?: 1; + + $random = (array) array_rand($attemptIdList, $round); + + $selection = []; + + foreach ($random as $rand) { + $selection[] = $attemptIdList[$rand]; + } + + return $selection; + } +} diff --git a/plugin/exercisefocused/templates/block.html.twig b/plugin/exercisefocused/templates/block.html.twig new file mode 100644 index 000000000..f86f72bd0 --- /dev/null +++ b/plugin/exercisefocused/templates/block.html.twig @@ -0,0 +1,105 @@ +{% if exercisefocused.show_region %} + {% set enable_time_limit = 'true' == exercisefocused.plugin_info.obj.get('enable_time_limit') %} + {% set time_limit = exercisefocused.plugin_info.obj.get('time_limit') %} + {% set enable_outfocused_limit = 'true' == exercisefocused.plugin_info.obj.get('enable_outfocused_limit') %} + {% set outfocused_limit = exercisefocused.plugin_info.obj.get('outfocused_limit') %} + + + +{% endif %} \ No newline at end of file diff --git a/plugin/exercisefocused/templates/script.html.twig b/plugin/exercisefocused/templates/script.html.twig new file mode 100644 index 000000000..d849c6cc5 --- /dev/null +++ b/plugin/exercisefocused/templates/script.html.twig @@ -0,0 +1,163 @@ +{% if exercisefocused.show_region %} + {% set enable_time_limit = 'true' == exercisefocused.plugin_info.obj.get('enable_time_limit') %} + {% set time_limit = exercisefocused.plugin_info.obj.get('time_limit') %} + {% set enable_outfocused_limit = 'true' == exercisefocused.plugin_info.obj.get('enable_outfocused_limit') %} + {% set outfocused_limit = exercisefocused.plugin_info.obj.get('outfocused_limit') %} + + {% set ALL_ON_ONE_PAGE = exercisefocused.exercise_type == 1 %} + {% set ONE_PER_PAGE = exercisefocused.exercise_type == 2 %} + + +{% endif %} \ No newline at end of file diff --git a/plugin/exercisefocused/uninstall.php b/plugin/exercisefocused/uninstall.php new file mode 100644 index 000000000..7a0396b9f --- /dev/null +++ b/plugin/exercisefocused/uninstall.php @@ -0,0 +1,5 @@ +uninstall(); diff --git a/plugin/exercisemonitoring/admin.php b/plugin/exercisemonitoring/admin.php new file mode 100644 index 000000000..4210e607a --- /dev/null +++ b/plugin/exercisemonitoring/admin.php @@ -0,0 +1,3 @@ +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 Mz|eplW8%~Q0RppA!2kdN literal 0 HcmV?d00001 diff --git a/plugin/exercisemonitoring/assets/images/user.png b/plugin/exercisemonitoring/assets/images/user.png new file mode 100644 index 0000000000000000000000000000000000000000..eed8a246f79d590be8d0f3b8ead3fa1a832f8c8c GIT binary patch literal 9957 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::class); +$trackExeRepo = $em->getRepository(TrackEExercises::class); + +$lifetimeDays = (int) $plugin->get(ExerciseMonitoringPlugin::SETTING_SNAPSHOTS_LIFETIME); + +if (empty($lifetimeDays)) { + logging("There is no set time limit"); + exit; +} + +$timeLimit = api_get_utc_datetime(null, false, true); +$timeLimit->modify("-$lifetimeDays day"); + +logging( + sprintf("Deleting snapshots taken before than %s", $timeLimit->format('Y-m-d H:i:s')) +); + +$fs = new Filesystem(); + +$logs = findLogsBeforeThan($timeLimit); + +foreach ($logs as $log) { + $sysPath = ExerciseMonitoringPlugin::generateSnapshotUrl( + $log['exe_user_id'], + $log['image_filename'], + SYS_UPLOAD_PATH + ); + + if (!file_exists($sysPath)) { + logging( + sprintf("File %s not exists", $sysPath) + ); + + continue; + } + + $fs->remove($sysPath); + + Database::update( + 'plugin_exercisemonitoring_log', + ['removed' => true], + ['id = ?' => $log['log_id']] + ); + + logging( + sprintf( + "From exe_id %s; deleting filename %s created at %s", + $log['exe_id'], + $sysPath, + $log['created_at'] + ) + ); +} + +function findLogsBeforeThan(DateTime $timeLimit): array +{ + $sql = "SELECT tee.exe_id, l.id AS log_id, l.image_filename, tee.exe_user_id + FROM plugin_exercisemonitoring_log l + INNER JOIN chamilo.track_e_exercises tee on l.exe_id = tee.exe_id + WHERE l.created_at <= '".$timeLimit->format('Y-m-d H:i:s')."' + AND l.removed IS FALSE"; + + $result = Database::query($sql); + + $rows = []; + + while ($row = Database::fetch_assoc($result)) { + $rows[] = $row; + } + + return $rows; +} + +function logging(string $message) +{ + $time = time(); + + printf("[%s] %s \n", $time, $message); +} diff --git a/plugin/exercisemonitoring/index.php b/plugin/exercisemonitoring/index.php new file mode 100644 index 000000000..2e7af8ad1 --- /dev/null +++ b/plugin/exercisemonitoring/index.php @@ -0,0 +1,61 @@ +isEnabled(true); +$showOverviewRegion = $isEnabled && strpos($_SERVER['SCRIPT_NAME'], '/main/exercise/overview.php') !== false; +$showSubmitRegion = $isEnabled && strpos($_SERVER['SCRIPT_NAME'], '/main/exercise/exercise_submit.php') !== false; + +$_template['enabled'] = false; +$_template['show_overview_region'] = $showOverviewRegion; +$_template['show_submit_region'] = $showSubmitRegion; + +if ($showOverviewRegion || $showSubmitRegion) { + $exerciseId = (int) $_GET['exerciseId']; + + $objFieldValue = new ExtraFieldValue('exercise'); + $values = $objFieldValue->get_values_by_handler_and_field_variable( + $exerciseId, + ExerciseMonitoringPlugin::FIELD_SELECTED + ); + + $_template['enabled'] = $values && (bool) $values['value']; + $_template['exercise_id'] = $exerciseId; +} + +$_template['enable_snapshots'] = true; + +$isAdult = $plugin->isAdult(); + +if ($showOverviewRegion && $_template['enabled']) { + $_template['instructions'] = $plugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTIONS); + + if ('true' === $plugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTION_AGE_DISTINCTION_ENABLE)) { + $_template['instructions'] = $plugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTIONS_MINORS); + + if ($isAdult) { + $_template['instructions'] = $plugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTIONS_ADULTS); + } else { + $_template['enable_snapshots'] = false; + } + } + + $_template['instructions'] = Security::remove_XSS($_template['instructions']); +} + +if ($showSubmitRegion && $_template['enabled']) { + $exercise = new Exercise(api_get_course_int_id()); + + if ($exercise->read($_template['exercise_id'])) { + $_template['exercise_type'] = (int) $exercise->selectType(); + + if ('true' === $plugin->get(ExerciseMonitoringPlugin::SETTING_INSTRUCTION_AGE_DISTINCTION_ENABLE) + && !$isAdult + ) { + $_template['enable_snapshots'] = false; + } + } +} diff --git a/plugin/exercisemonitoring/install.php b/plugin/exercisemonitoring/install.php new file mode 100644 index 000000000..a9af675a7 --- /dev/null +++ b/plugin/exercisemonitoring/install.php @@ -0,0 +1,5 @@ +install(); diff --git a/plugin/exercisemonitoring/lang/english.php b/plugin/exercisemonitoring/lang/english.php new file mode 100644 index 000000000..25a1ee89e --- /dev/null +++ b/plugin/exercisemonitoring/lang/english.php @@ -0,0 +1,32 @@ +birthdate'; +$strings['instructions_adults'] = 'Intructions for adults students'; +$strings['instructions_minors'] = 'Intrucctions for minors students'; +$strings['snapshots_lifetime'] = 'Life time of photos taken'; +$strings['snapshots_lifetime_help'] = 'Number of days that taken photos can remain stored on the server.
    The cleanup script is located in plugin/exercisemonitoring/cron/cleanup.php'; + +$strings['ExerciseMonitored'] = "Exercise monitored"; +$strings['Retry'] = "Retry"; +$strings['IdDocumentSnapshot'] = "Validated photo of the ID document"; +$strings['StudentSnapshot'] = "Validated photo of the student"; + +$strings['ImageIdDocumentCameraInstructions'] = "Place your ID document in front of the camera and place it in the marked box. Click the Capture button or press the space bar on your keyboard."; +$strings['ImageStudentCameraInstructions'] = "Place your face in front of the camera and place it within the marked circle. Click the Capture button or press the space bar on your keyboard"; + +$strings['Snapshots'] = "Snapshots"; + +$strings['ExerciseUnmonitored'] = "Exercise unmonitored"; +$strings['Birthdate'] = "Birthdate"; +$strings['AdultStudent'] = "Adult student"; +$strings['MinorStudent'] = "Minor student"; diff --git a/plugin/exercisemonitoring/lang/spanish.php b/plugin/exercisemonitoring/lang/spanish.php new file mode 100644 index 000000000..0f5ff8c2c --- /dev/null +++ b/plugin/exercisemonitoring/lang/spanish.php @@ -0,0 +1,32 @@ +birthdate'; +$strings['instructions_adults'] = 'Intrucciones para estudiantes adultos'; +$strings['instructions_minors'] = 'Intrucciones para estudiantes menores de edad'; +$strings['snapshots_lifetime'] = 'Tiempo de vida de las fotos tomadas'; +$strings['snapshots_lifetime_help'] = 'Cantidad de días que las fotos tomadas pueden permanecer almacenadas en el servidor.
    El script de limpieza está ubicado en plugin/exercisemonitoring/cron/cleanup.php'; + +$strings['ExerciseMonitored'] = "Ejercicio monitoreado"; +$strings['Retry'] = "Reintentar"; +$strings['IdDocumentSnapshot'] = "Foto validada del documento de identidad"; +$strings['StudentSnapshot'] = "Foto validada del estudiante"; + +$strings['ImageIdDocumentCameraInstructions'] = "Coloca tu DNI o documento de identidad frente a la cámara y ubícalo en el recuadro marcado. Dale clic al botón Capturar o presiona la barra de espacio de tu teclado."; +$strings['ImageStudentCameraInstructions'] = "Coloca tu rostro frente a la cámara y ubícalo dentro del círculo marcado. Dale click al botón Capturar o presiona la barra de espacio de tu teclado"; + +$strings['Snapshots'] = "Fotos tomadas"; + +$strings['ExerciseUnmonitored'] = "Ejercicio no monitoreado"; +$strings['Birthdate'] = "Fecha de nacimiento"; +$strings['AdultStudent'] = "Estudiante adulto"; +$strings['MinorStudent'] = "Estudiante menor de edad"; diff --git a/plugin/exercisemonitoring/pages/detail.php b/plugin/exercisemonitoring/pages/detail.php new file mode 100644 index 000000000..33b471f94 --- /dev/null +++ b/plugin/exercisemonitoring/pages/detail.php @@ -0,0 +1,32 @@ +getRepository(Log::class); + +$detailController = new DetailController( + ExerciseMonitoringPlugin::create(), + HttpRequest::createFromGlobals(), + $em, + $logRepository +); + +try { + $response = $detailController(); +} catch (Exception $e) { + $response = HttpResponse::create('', HttpResponse::HTTP_FORBIDDEN); +} + +$response->send(); diff --git a/plugin/exercisemonitoring/pages/exercise_submit.ajax.php b/plugin/exercisemonitoring/pages/exercise_submit.ajax.php new file mode 100644 index 000000000..471f90868 --- /dev/null +++ b/plugin/exercisemonitoring/pages/exercise_submit.ajax.php @@ -0,0 +1,18 @@ +send(); diff --git a/plugin/exercisemonitoring/pages/start.ajax.php b/plugin/exercisemonitoring/pages/start.ajax.php new file mode 100644 index 000000000..3832106e8 --- /dev/null +++ b/plugin/exercisemonitoring/pages/start.ajax.php @@ -0,0 +1,18 @@ +send(); diff --git a/plugin/exercisemonitoring/plugin.php b/plugin/exercisemonitoring/plugin.php new file mode 100644 index 000000000..391c44e50 --- /dev/null +++ b/plugin/exercisemonitoring/plugin.php @@ -0,0 +1,10 @@ +get_info(); + +$plugin_info['templates'] = [ + 'templates/modal.html.twig', + 'templates/exercise_submit.html.twig', +]; diff --git a/plugin/exercisemonitoring/src/Controller/DetailController.php b/plugin/exercisemonitoring/src/Controller/DetailController.php new file mode 100644 index 000000000..e9c5b05e7 --- /dev/null +++ b/plugin/exercisemonitoring/src/Controller/DetailController.php @@ -0,0 +1,111 @@ +plugin = $plugin; + $this->request = $request; + $this->em = $em; + $this->logRepository = $logRepository; + } + + /** + * @throws Exception + */ + public function __invoke(): HttpResponse + { + if (!$this->plugin->isEnabled(true)) { + throw new Exception(); + } + + $trackExe = $this->em->find( + TrackEExercises::class, + $this->request->query->getInt('id') + ); + + if (!$trackExe) { + throw new Exception(); + } + + $exercise = $this->em->find(CQuiz::class, $trackExe->getExeExoId()); + $user = api_get_user_entity($trackExe->getExeUserId()); + + $objExercise = new Exercise($trackExe->getCId()); + $objExercise->read($trackExe->getExeExoId()); + + $logs = $this->logRepository->findSnapshots($objExercise, $trackExe); + + $content = $this->generateHeader($objExercise, $user, $trackExe) + .'
    ' + .$this->generateSnapshotList($logs, $trackExe->getExeUserId()); + + return HttpResponse::create($content); + } + + private function generateSnapshotList(array $logs, int $userId): string + { + $html = ''; + + foreach ($logs as $i => $log) { + $date = api_get_local_time($log['createdAt'], null, null, true, true, true); + + $html .= '
    '; + $html .= '
    '; + $html .= Display::img( + ExerciseMonitoringPlugin::generateSnapshotUrl($userId, $log['imageFilename']), + $date + ); + $html .= '
    '; + $html .= Display::tag('p', $date, ['class' => 'text-center']); + $html .= Display::tag('div', $log['log_level'], ['class' => 'text-center']); + $html .= '
    '; + $html .= '
    '; + $html .= '
    '; + } + + return '
    '.$html.'
    '; + } +} diff --git a/plugin/exercisemonitoring/src/Controller/ExerciseSubmitController.php b/plugin/exercisemonitoring/src/Controller/ExerciseSubmitController.php new file mode 100644 index 000000000..94e54aa43 --- /dev/null +++ b/plugin/exercisemonitoring/src/Controller/ExerciseSubmitController.php @@ -0,0 +1,119 @@ +plugin = $plugin; + $this->request = $request; + $this->em = $em; + } + + /** + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\TransactionRequiredException + */ + public function __invoke(): HttpResponse + { + $userDirName = $this->createDirectory(); + + $existingExeId = (int) ChamiloSession::read('exe_id'); + + $levelId = $this->request->request->getInt('level_id'); + $exerciseId = $this->request->request->getInt('exercise_id'); + + $exercise = $this->em->find(CQuiz::class, $exerciseId); + + $objExercise = new Exercise(); + $objExercise->read($exerciseId); + + $trackingExercise = $this->em->find(TrackEExercises::class, $existingExeId); + + $newFilename = ''; + $level = 0; + + /** @var UploadedFile $imgSubmit */ + if ($imgSubmit = $this->request->files->get('snapshot')) { + $newFilename = uniqid().'_submit.jpg'; + + $imgSubmit->move($userDirName, $newFilename); + } + + if (ONE_PER_PAGE == $objExercise->selectType()) { + $question = $this->em->find(CQuizQuestion::class, $levelId); + $level = $question->getIid(); + } + + $log = new Log(); + $log + ->setExercise($exercise) + ->setExe($trackingExercise) + ->setLevel($level) + ->setImageFilename($newFilename) + ; + + $this->em->persist($log); + + $this->updateOrphanSnapshots($exercise, $trackingExercise); + + $this->em->flush(); + + return HttpResponse::create(); + } + + private function createDirectory(): string + { + $user = api_get_user_entity(api_get_user_id()); + + $pluginDirName = api_get_path(SYS_UPLOAD_PATH).'plugins/exercisemonitoring'; + $userDirName = $pluginDirName.'/'.$user->getId(); + + $fs = new Filesystem(); + $fs->mkdir( + [$pluginDirName, $userDirName], + api_get_permissions_for_new_directories() + ); + + return $userDirName; + } + + private function updateOrphanSnapshots(CQuiz $exercise, TrackEExercises $trackingExe) + { + $repo = $this->em->getRepository(Log::class); + + $fileNamesToUpdate = ChamiloSession::read($this->plugin->get_name().'_orphan_snapshots', []); + + if (empty($fileNamesToUpdate)) { + return; + } + + foreach ($fileNamesToUpdate as $filename) { + $log = $repo->findOneBy(['imageFilename' => $filename, 'exercise' => $exercise, 'exe' => null]); + + if (!$log) { + continue; + } + + $log->setExe($trackingExe); + } + + ChamiloSession::erase($this->plugin->get_name().'_orphan_snapshots'); + } +} diff --git a/plugin/exercisemonitoring/src/Controller/StartController.php b/plugin/exercisemonitoring/src/Controller/StartController.php new file mode 100644 index 000000000..408a551d4 --- /dev/null +++ b/plugin/exercisemonitoring/src/Controller/StartController.php @@ -0,0 +1,93 @@ +plugin = $plugin; + $this->request = $request; + $this->em = $em; + } + + public function __invoke(): HttpResponse + { + $userDirName = $this->createDirectory(); + + /** @var UploadedFile $imgIddoc */ + $imgIddoc = $this->request->files->get('iddoc'); + /** @var UploadedFile $imgLearner */ + $imgLearner = $this->request->files->get('learner'); + + $exercise = $this->em->find(CQuiz::class, $this->request->request->getInt('exercise_id')); + + $fileNamesToUpdate = []; + + if ($imgIddoc) { + $newFilename = uniqid().'_iddoc.jpg'; + $fileNamesToUpdate[] = $newFilename; + + $imgIddoc->move($userDirName, $newFilename); + + $log = new Log(); + $log + ->setExercise($exercise) + ->setLevel(-1) + ->setImageFilename($newFilename) + ; + + $this->em->persist($log); + } + + if ($imgLearner) { + $newFilename = uniqid().'_learner.jpg'; + $fileNamesToUpdate[] = $newFilename; + + $imgLearner->move($userDirName, $newFilename); + + $log = new Log(); + $log + ->setExercise($exercise) + ->setLevel(-1) + ->setImageFilename($newFilename) + ; + + $this->em->persist($log); + } + + $this->em->flush(); + + ChamiloSession::write($this->plugin->get_name().'_orphan_snapshots', $fileNamesToUpdate); + + return HttpResponse::create(); + } + + private function createDirectory(): string + { + $user = api_get_user_entity(api_get_user_id()); + + $pluginDirName = api_get_path(SYS_UPLOAD_PATH).'plugins/exercisemonitoring'; + $userDirName = $pluginDirName.'/'.$user->getId(); + + $fs = new Filesystem(); + $fs->mkdir( + [$pluginDirName, $userDirName], + api_get_permissions_for_new_directories() + ); + + return $userDirName; + } +} diff --git a/plugin/exercisemonitoring/src/Entity/Log.php b/plugin/exercisemonitoring/src/Entity/Log.php new file mode 100644 index 000000000..81a629168 --- /dev/null +++ b/plugin/exercisemonitoring/src/Entity/Log.php @@ -0,0 +1,133 @@ +removed = false; + } + + public function getId(): int + { + return $this->id; + } + + public function getExercise(): CQuiz + { + return $this->exercise; + } + + public function setExercise(CQuiz $exercise): Log + { + $this->exercise = $exercise; + + return $this; + } + + public function getExe(): ?TrackEExercises + { + return $this->exe; + } + + public function setExe(?TrackEExercises $exe): Log + { + $this->exe = $exe; + + return $this; + } + + public function getLevel(): int + { + return $this->level; + } + + public function setLevel(int $level): Log + { + $this->level = $level; + + return $this; + } + + public function getImageFilename(): string + { + return $this->imageFilename; + } + + public function setImageFilename(string $imageFilename): Log + { + $this->imageFilename = $imageFilename; + + return $this; + } + + public function isRemoved(): bool + { + return $this->removed; + } + + public function setRemoved(bool $removed): void + { + $this->removed = $removed; + } +} diff --git a/plugin/exercisemonitoring/src/ExerciseMonitoringPlugin.php b/plugin/exercisemonitoring/src/ExerciseMonitoringPlugin.php new file mode 100644 index 000000000..997885e96 --- /dev/null +++ b/plugin/exercisemonitoring/src/ExerciseMonitoringPlugin.php @@ -0,0 +1,185 @@ + 'boolean', + self::SETTING_INSTRUCTIONS => 'wysiwyg', + self::SETTING_INSTRUCTION_AGE_DISTINCTION_ENABLE => 'boolean', + self::SETTING_INSTRUCTION_LEGAL_AGE => 'text', + self::SETTING_EXTRAFIELD_BIRTHDATE => 'text', + self::SETTING_INSTRUCTIONS_ADULTS => 'wysiwyg', + self::SETTING_INSTRUCTIONS_MINORS => 'wysiwyg', + self::SETTING_SNAPSHOTS_LIFETIME => 'text', + ]; + + parent::__construct( + $version, + "Angel Fernando Quiroz Campos ", + $settings + ); + } + + public static function create(): self + { + static $result = null; + + return $result ?: $result = new self(); + } + + /** + * @throws ToolsException + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_LOG])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Log::class), + ] + ); + + $pluginDirName = api_get_path(SYS_UPLOAD_PATH).'plugins/exercisemonitoring'; + + $fs = new Filesystem(); + $fs->mkdir( + $pluginDirName, + api_get_permissions_for_new_directories() + ); + + $objField = new ExtraField('exercise'); + $objField->save([ + 'variable' => self::FIELD_SELECTED, + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + 'display_text' => $this->get_title(), + 'visible_to_self' => true, + 'changeable' => true, + 'filter' => false, + ]); + } + + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_LOG])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Log::class), + ] + ); + + $objField = new ExtraField('exercise'); + $extraFieldInfo = $objField->get_handler_field_info_by_field_variable(self::FIELD_SELECTED); + + if ($extraFieldInfo) { + $objField->delete($extraFieldInfo['id']); + } + } + + public function getAdminUrl(): string + { + $name = $this->get_name(); + $webPath = api_get_path(WEB_PLUGIN_PATH).$name; + + return "$webPath/admin.php"; + } + + public function generateDetailLink(int $exeId, int $userId): string + { + $title = $this->get_lang('ExerciseMonitored'); + $webcamIcon = Display::return_icon('webcam.png', $title); + $webcamNaIcon = Display::return_icon('webcam_na.png', $this->get_lang('ExerciseUnmonitored')); + + $monitoringDetailUrl = api_get_path(WEB_PLUGIN_PATH).'exercisemonitoring/pages/detail.php?'.api_get_cidreq() + .'&'.http_build_query(['id' => $exeId]); + + $url = Display::url( + $webcamIcon, + $monitoringDetailUrl, + [ + 'class' => 'ajax', + 'data-title' => $title, + 'data-size' => 'lg', + ] + ); + + $showLink = true; + + if ('true' === $this->get(self::SETTING_INSTRUCTION_AGE_DISTINCTION_ENABLE) && !$this->isAdult($userId)) { + $showLink = false; + } + + return $showLink ? $url : $webcamNaIcon; + } + + public static function generateSnapshotUrl( + int $userId, + string $imageFileName, + string $path = WEB_UPLOAD_PATH + ): string { + $pluginDirName = api_get_path($path).'plugins/exercisemonitoring'; + + return $pluginDirName.'/'.$userId.'/'.$imageFileName; + } + + /** + * @throws Exception + */ + public function isAdult(int $userId = 0): bool + { + $userId = $userId ?: api_get_user_id(); + $fieldVariable = $this->get(self::SETTING_EXTRAFIELD_BIRTHDATE); + $legalAge = (int) $this->get(self::SETTING_INSTRUCTION_LEGAL_AGE); + + $value = UserManager::get_extra_user_data_by_field($userId, $fieldVariable); + + if (empty($value)) { + return false; + } + + if (empty($value[$fieldVariable])) { + return false; + } + + $birthdate = new DateTime($value[$fieldVariable]); + $now = new DateTime(); + $diff = $birthdate->diff($now); + + return !$diff->invert && $diff->y >= $legalAge; + } +} diff --git a/plugin/exercisemonitoring/src/Repository/LogRepository.php b/plugin/exercisemonitoring/src/Repository/LogRepository.php new file mode 100644 index 000000000..f307f97d0 --- /dev/null +++ b/plugin/exercisemonitoring/src/Repository/LogRepository.php @@ -0,0 +1,47 @@ +findBy( + [ + 'level' => $level, + 'exe' => $exe, + ], + ['createdAt' => 'ASC'] + ); + } + + public function findSnapshots(Exercise $objExercise, TrackEExercises $trackExe) + { + $qb = $this->createQueryBuilder('l'); + + $qb->select(['l.imageFilename', 'l.createdAt']); + + if (ONE_PER_PAGE == $objExercise->selectType()) { + $qb + ->addSelect(['qq.question AS log_level']) + ->leftJoin(CQuizQuestion::class, 'qq', Join::WITH, 'l.level = qq.iid'); + } + + $query = $qb + ->andWhere( + $qb->expr()->eq('l.exe', $trackExe->getExeId()) + ) + ->addOrderBy('l.createdAt') + ->getQuery(); + + return $query->getResult(); + } +} diff --git a/plugin/exercisemonitoring/templates/exercise_submit.html.twig b/plugin/exercisemonitoring/templates/exercise_submit.html.twig new file mode 100644 index 000000000..6430d8a45 --- /dev/null +++ b/plugin/exercisemonitoring/templates/exercise_submit.html.twig @@ -0,0 +1,84 @@ +{% if exercisemonitoring.show_submit_region and exercisemonitoring.enabled and exercisemonitoring.enable_snapshots %} + {% set ALL_ON_ONE_PAGE = exercisemonitoring.exercise_type == 1 %} + {% set ONE_PER_PAGE = exercisemonitoring.exercise_type == 2 %} + +
    + + + + +{% endif %} diff --git a/plugin/exercisemonitoring/templates/modal.html.twig b/plugin/exercisemonitoring/templates/modal.html.twig new file mode 100644 index 000000000..18a14c831 --- /dev/null +++ b/plugin/exercisemonitoring/templates/modal.html.twig @@ -0,0 +1,314 @@ +{% if exercisemonitoring.show_overview_region and exercisemonitoring.enabled %} + {% if exercisemonitoring.enable_snapshots %} + + + + + + {% else %} + + + {% endif %} +{% endif %} \ No newline at end of file diff --git a/plugin/exercisemonitoring/uninstall.php b/plugin/exercisemonitoring/uninstall.php new file mode 100644 index 000000000..4201e82f9 --- /dev/null +++ b/plugin/exercisemonitoring/uninstall.php @@ -0,0 +1,5 @@ +uninstall(); diff --git a/plugin/ext_auth_chamilo_logout_button_behaviour/README.md b/plugin/ext_auth_chamilo_logout_button_behaviour/README.md new file mode 100644 index 000000000..ce6532f1a --- /dev/null +++ b/plugin/ext_auth_chamilo_logout_button_behaviour/README.md @@ -0,0 +1,20 @@ +External Authentication Logout button behaviour plugin +=== + +If you use some other authentication than local Chamilo authentication, you may want to disable the Chamilo logout button and display information about your SSO logout. + +With this plugin, you can: + +* change the logout URL of the button +* disable the logout button +* display it in grey style +* change the button tooltip on mouseover +* add an alert message if user click on the button + +Related plugins +--- + +Add a button to login using CAS
    +Add a button to logout from CAS
    +Add a button to login using FACEBOOK account
    +Add a button to login using Shibboleth
    diff --git a/plugin/ext_auth_chamilo_logout_button_behaviour/index.php b/plugin/ext_auth_chamilo_logout_button_behaviour/index.php new file mode 100644 index 000000000..690fc3119 --- /dev/null +++ b/plugin/ext_auth_chamilo_logout_button_behaviour/index.php @@ -0,0 +1,42 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +//the plugin title +$plugin_info['title'] = 'Enable or disable logout button'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If you use some other authentication than local Chamilo authentication, you may have to disable the Chamilo logout button and give users information about your SSO logout."; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Hubert Borderiou'; +//the plugin configuration +$form = new FormValidator('eaclbb_form'); +$form->addElement("html", "
    Fill the text boxes below to overwrite the default values used in this plugin
    "); +$form->addElement('text', 'eaclbb_form_link_url', 'Page to load when clicking on the logout button'); +$form->addElement('text', 'eaclbb_form_link_infobulle', 'Tooltip text for the logout button (displayed on mouseover)'); +$form->addElement('checkbox', 'eaclbb_form_link_image', "Display the logout button disabled (black and white)", "", ['checked' => true]); +$form->addElement('checkbox', 'eaclbb_form_alert_onoff', "Display an alert when clicking on the logout button", "", ['checked' => true]); +$form->addElement('text', 'eaclbb_form_alert_text', "Text displayed in the alert box when clickng on the logout button (if checkbox above has been checked)."); + +$form->addButtonSave(get_lang('Save'), 'submit_button'); +//get default value for form +$tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_url = api_get_setting('ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_url'); +$tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_infobulle = api_get_setting('ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_infobulle'); +$tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_image = api_get_setting('ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_image'); +$tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_onoff = api_get_setting('ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_onoff'); +$tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_text = api_get_setting('ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_text'); +if ($tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_url) { + $defaults['eaclbb_form_link_url'] = $tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_url['eaclbb_form_link_url']; +} + +if ($tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_infobulle) { + $defaults['eaclbb_form_link_infobulle'] = $tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_infobulle['eaclbb_form_link_infobulle']; +} + +if ($tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_image) { + $defaults['eaclbb_form_link_image'] = $tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_link_image['eaclbb_form_link_image']; +} +if ($tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_onoff) { + $defaults['eaclbb_form_alert_onoff'] = $tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_onoff['eaclbb_form_alert_onoff']; +} +if ($tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_text) { + $defaults['eaclbb_form_alert_text'] = $tab_default_ext_auth_chamilo_logout_button_behaviour_eaclbb_form_alert_text['eaclbb_form_alert_text']; +} +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +// Set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/ext_auth_chamilo_logout_button_behaviour/template.tpl b/plugin/ext_auth_chamilo_logout_button_behaviour/template.tpl new file mode 100644 index 000000000..c9352e9f4 --- /dev/null +++ b/plugin/ext_auth_chamilo_logout_button_behaviour/template.tpl @@ -0,0 +1,12 @@ +{% if ext_auth_chamilo_logout_button_behaviour.show_message %} + +{% endif %} diff --git a/plugin/externalnotificationconnect/README.md b/plugin/externalnotificationconnect/README.md new file mode 100644 index 000000000..01db07625 --- /dev/null +++ b/plugin/externalnotificationconnect/README.md @@ -0,0 +1,22 @@ +# External Notification Connect + +Activate external notification system, that will send a notification to an external REST webservice on a specific Chamilo's action trigger. + +At the moment it will send notifications on : + +* Learning path creation (Chamilo LP or Scorm import) +* Portfolio post creation, deletion or edition + +For creation notifications, it will send : + +* user_id : id of the user creating the item +* content_id : internal Chamilo id of the item created +* content_url : URL to see the content +* content_title : title of the item +* course_code : code of the course in Chamilo in which the item as been created +* content_type : 'eportfolio' or 'lp' + +For editions and deletions, it will send : + +* content_id : internal Chamilo id of the item +* content_type : 'eportfolio' or 'lp' diff --git a/plugin/externalnotificationconnect/install.php b/plugin/externalnotificationconnect/install.php new file mode 100644 index 000000000..357e861b3 --- /dev/null +++ b/plugin/externalnotificationconnect/install.php @@ -0,0 +1,5 @@ +install(); diff --git a/plugin/externalnotificationconnect/lang/english.php b/plugin/externalnotificationconnect/lang/english.php new file mode 100644 index 000000000..02b2ada68 --- /dev/null +++ b/plugin/externalnotificationconnect/lang/english.php @@ -0,0 +1,18 @@ +Yes
    to activate external notification for Portfolio tool actions'; +$strings['notify_learnpath'] = 'Learning path'; +$strings['notify_learnpath_help'] = 'Put it to Yes to activate external notification for learning path creation'; diff --git a/plugin/externalnotificationconnect/lang/french.php b/plugin/externalnotificationconnect/lang/french.php new file mode 100644 index 000000000..c60bfb2f8 --- /dev/null +++ b/plugin/externalnotificationconnect/lang/french.php @@ -0,0 +1,18 @@ +Oui pour activer les notifications pour les actions du Portfolio'; +$strings['notify_learnpath'] = 'Parcours'; +$strings['notify_learnpath_help'] = 'Mettre à Oui pour activer les notifications pour la création de parcours'; diff --git a/plugin/externalnotificationconnect/lang/spanish.php b/plugin/externalnotificationconnect/lang/spanish.php new file mode 100644 index 000000000..443fabbae --- /dev/null +++ b/plugin/externalnotificationconnect/lang/spanish.php @@ -0,0 +1,18 @@ +Sí para activar la notificación externa para las acciones de la herramienta Portafolio'; +$strings['notify_learnpath'] = 'Lecciones'; +$strings['notify_learnpath_help'] = 'Ponerlo a para activar la notificación externa para la creación de lecciones'; diff --git a/plugin/externalnotificationconnect/plugin.php b/plugin/externalnotificationconnect/plugin.php new file mode 100644 index 000000000..c64e35c95 --- /dev/null +++ b/plugin/externalnotificationconnect/plugin.php @@ -0,0 +1,5 @@ +get_info(); diff --git a/plugin/externalnotificationconnect/src/Entity/AccessToken.php b/plugin/externalnotificationconnect/src/Entity/AccessToken.php new file mode 100644 index 000000000..fc47960e6 --- /dev/null +++ b/plugin/externalnotificationconnect/src/Entity/AccessToken.php @@ -0,0 +1,71 @@ +id; + } + + public function setId(int $id): AccessToken + { + $this->id = $id; + + return $this; + } + + public function getToken(): string + { + return $this->token; + } + + public function setToken(string $token): AccessToken + { + $this->token = $token; + + return $this; + } + + public function isValid(): bool + { + return $this->isValid; + } + + public function setIsValid(bool $isValid): AccessToken + { + $this->isValid = $isValid; + + return $this; + } +} diff --git a/plugin/externalnotificationconnect/src/ExternalNotificationConnectPlugin.php b/plugin/externalnotificationconnect/src/ExternalNotificationConnectPlugin.php new file mode 100644 index 000000000..1cb1f52c4 --- /dev/null +++ b/plugin/externalnotificationconnect/src/ExternalNotificationConnectPlugin.php @@ -0,0 +1,241 @@ +', + ]; + + $settings = [ + 'tool_enable' => 'boolean', + self::SETTING_AUTH_URL => 'text', + self::SETTING_AUTH_USERNAME => 'text', + self::SETTING_AUTH_PASSWORD => 'text', + self::SETTING_NOTIFICATION_URL => 'text', + self::SETTING_NOTIFY_PORTFOLIO => 'boolean', + self::SETTING_NOTIFY_LEARNPATH => 'boolean', + ]; + + parent::__construct( + '1.0', + implode('; ', $author), + $settings + ); + } + + public static function create(): ?ExternalNotificationConnectPlugin + { + static $result = null; + + return $result ?: $result = new self(); + } + + public function performActionsAfterConfigure(): ExternalNotificationConnectPlugin + { + $portfolioItemAddedEvent = HookPortfolioItemAdded::create(); + $portfolioItemEditedEvent = HookPortfolioItemEdited::create(); + $portfolioItemDeletedEvent = HookPortfolioItemDeleted::create(); + $portfolioItemVisibilityEvent = HookPortfolioItemVisibility::create(); + + $portfolioItemAddedObserver = ExternalNotificationConnectPortfolioItemAddedHookObserver::create(); + $portfolioItemEditedObserver = ExternalNotificationConnectPortfolioItemEditedHookObserver::create(); + $portfolioItemDeletedObserver = ExternalNotificationConnectPortfolioItemDeletedHookObserver::create(); + $portfolioItemVisibilityObserver = ExternalNotificationConnectPortfolioItemVisibilityHookObserver::create(); + + if ('true' === $this->get(self::SETTING_NOTIFY_PORTFOLIO)) { + $portfolioItemAddedEvent->attach($portfolioItemAddedObserver); + $portfolioItemEditedEvent->attach($portfolioItemEditedObserver); + $portfolioItemDeletedEvent->attach($portfolioItemDeletedObserver); + $portfolioItemVisibilityEvent->attach($portfolioItemVisibilityObserver); + } else { + $portfolioItemAddedEvent->detach($portfolioItemAddedObserver); + $portfolioItemEditedEvent->detach($portfolioItemEditedObserver); + $portfolioItemDeletedEvent->detach($portfolioItemDeletedObserver); + $portfolioItemVisibilityEvent->detach($portfolioItemVisibilityObserver); + } + + $lpCreatedEvent = HookLearningPathCreated::create(); + + $lpCreatedObserver = ExternalNotificationConnectLearningPathCreatedHookObserver::create(); + + if ('true' === $this->get(self::SETTING_NOTIFY_LEARNPATH)) { + $lpCreatedEvent->attach($lpCreatedObserver); + } else { + $lpCreatedEvent->detach($lpCreatedObserver); + } + + return $this; + } + + public function installHook() + { + } + + public function uninstallHook() + { + $portfolioItemAddedEvent = HookPortfolioItemAdded::create(); + $portfolioItemEditedEvent = HookPortfolioItemEdited::create(); + $portfolioItemDeletedEvent = HookPortfolioItemDeleted::create(); + $portfolioItemVisibilityEvent = HookPortfolioItemVisibility::create(); + $lpCreatedEvent = HookLearningPathCreated::create(); + + $portfolioItemAddedObserver = ExternalNotificationConnectPortfolioItemAddedHookObserver::create(); + $portfolioItemEditedObserver = ExternalNotificationConnectPortfolioItemEditedHookObserver::create(); + $portfolioItemDeletedObserver = ExternalNotificationConnectPortfolioItemDeletedHookObserver::create(); + $portfolioItemVisibilityObserver = ExternalNotificationConnectPortfolioItemVisibilityHookObserver::create(); + $lpCreatedObserver = ExternalNotificationConnectLearningPathCreatedHookObserver::create(); + + $portfolioItemAddedEvent->detach($portfolioItemAddedObserver); + $portfolioItemEditedEvent->detach($portfolioItemEditedObserver); + $portfolioItemDeletedEvent->detach($portfolioItemDeletedObserver); + $portfolioItemVisibilityEvent->detach($portfolioItemVisibilityObserver); + $lpCreatedEvent->detach($lpCreatedObserver); + } + + public function install() + { + $em = Database::getManager(); + + $schemaManager = $em->getConnection()->getSchemaManager(); + + $tableExists = $schemaManager->tablesExist(['plugin_ext_notif_conn_access_token']); + + if ($tableExists) { + return; + } + + $this->installDBTables(); + $this->installHook(); + } + + public function uninstall() + { + $this->uninstallHook(); + $this->uninstallDBTables(); + } + + /** + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMException + * @throws Exception + */ + public function getAccessToken() + { + $em = Database::getManager(); + $tokenRepository = $em->getRepository(AccessToken::class); + + $accessToken = $tokenRepository->findOneBy(['isValid' => true]); + + if (!$accessToken) { + $newToken = $this->requestAuthToken(); + + $accessToken = (new AccessToken()) + ->setToken($newToken) + ->setIsValid(true); + + $em->persist($accessToken); + $em->flush(); + } else { + $tks = explode('.', $accessToken->getToken()); + + $payload = json_decode(JWT::urlsafeB64Decode($tks[1]), true); + + if (time() >= $payload['exp']) { + $accessToken->setIsValid(false); + + $newToken = $this->requestAuthToken(); + + $accessToken = (new AccessToken()) + ->setToken($newToken) + ->setIsValid(true); + + $em->persist($accessToken); + + $em->flush(); + } + } + + return $accessToken->getToken(); + } + + private function installDBTables() + { + $em = Database::getManager(); + + try { + (new SchemaTool($em)) + ->createSchema([ + $em->getClassMetadata(AccessToken::class), + ]); + } catch (ToolsException $e) { + return; + } + } + + private function uninstallDBTables() + { + $em = Database::getManager(); + + (new SchemaTool($em)) + ->dropSchema([ + $em->getClassMetadata(AccessToken::class), + ]); + } + + /** + * @throws Exception + */ + private function requestAuthToken(): string + { + $client = new Client(); + + try { + $response = $client->request( + 'POST', + $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_URL), + [ + 'json' => [ + 'email' => $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_USERNAME), + 'password' => $this->get(ExternalNotificationConnectPlugin::SETTING_AUTH_PASSWORD), + ], + ] + ); + } catch (ClientException|ServerException $e) { + if (!$e->hasResponse()) { + throw new Exception($e->getMessage()); + } + + $response = $e->getResponse(); + } catch (GuzzleException $e) { + throw new Exception($e->getMessage()); + } + + $json = json_decode((string) $response->getBody(), true); + + if (201 !== $json['status']) { + throw new Exception($json['message']); + } + + return $json['data']['data']['token']; + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectHookObserver.php new file mode 100644 index 000000000..56e46d875 --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectHookObserver.php @@ -0,0 +1,21 @@ +plugin = ExternalNotificationConnectPlugin::create(); + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectLearningPathCreatedHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectLearningPathCreatedHookObserver.php new file mode 100644 index 000000000..2f99843de --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectLearningPathCreatedHookObserver.php @@ -0,0 +1,56 @@ +getEventData()['lp']; + $userId = api_get_user_id(); + $courseCode = api_get_course_id(); + + $cidreq = api_get_cidreq(); + + $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'; + $url .= ($cidreq ? $cidreq.'&' : ''); + $url .= http_build_query( + [ + 'action' => 'view', + 'lp_id' => $lp->getIid(), + 'isStudentView' => 'true', + ] + ); + + try { + $json = $this->doCreateRequest( + [ + 'user_id' => $userId, + 'course_code' => $courseCode, + 'content_id' => $lp->getIid(), + 'content_type' => 'lp', + 'content_url' => $url, + 'post_title' => $lp->getName(), + ] + ); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + return; + } + + if (empty($json)) { + return; + } + + error_log('ExtNotifConn: Learning path created: ID '.$json['data']['notification_id']); + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemAddedHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemAddedHookObserver.php new file mode 100644 index 000000000..aa8fc11aa --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemAddedHookObserver.php @@ -0,0 +1,50 @@ +getEventData()['portfolio']; + $userId = api_get_user_id(); + $courseCode = api_get_course_id(); + + $cidreq = api_get_cidreq(); + + $url = api_get_path(WEB_CODE_PATH).'portfolio/index.php?'; + $url .= ($cidreq ? $cidreq.'&' : ''); + $url .= http_build_query(['action' => 'view', 'id' => $item->getId()]); + + try { + $json = $this->doCreateRequest( + [ + 'user_id' => $userId, + 'course_code' => $courseCode, + 'content_id' => $item->getId(), + 'content_type' => 'eportfolio', + 'content_url' => $url, + 'post_title' => $item->getTitle(), + ] + ); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + return; + } + + if (empty($json)) { + return; + } + + error_log('ExtNotifConn: Portfolio item created: ID '.$json['data']['notification_id']); + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemDeletedHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemDeletedHookObserver.php new file mode 100644 index 000000000..102f25f34 --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemDeletedHookObserver.php @@ -0,0 +1,33 @@ +getEventData()['item']; + + try { + $json = $this->doDeleteRequest($item->getId(), 'eportfolio'); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + return; + } + + if (empty($json)) { + return; + } + + error_log('ExtNotifConn: Portfolio item deleted: Status '.((int) $json['status'])); + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemEditedHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemEditedHookObserver.php new file mode 100644 index 000000000..cc32a5187 --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemEditedHookObserver.php @@ -0,0 +1,50 @@ +getEventData()['item']; + $userId = api_get_user_id(); + $courseCode = api_get_course_id(); + + $cidreq = api_get_cidreq(); + + $url = api_get_path(WEB_CODE_PATH).'portfolio/index.php?'; + $url .= ($cidreq ? $cidreq.'&' : ''); + $url .= http_build_query(['action' => 'view', 'id' => $item->getId()]); + + try { + $json = $this->doEditRequest( + [ + 'user_id' => $userId, + 'course_code' => $courseCode, + 'content_id' => $item->getId(), + 'content_type' => 'eportfolio', + 'content_url' => $url, + 'post_title' => $item->getTitle(), + ] + ); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + return; + } + + if (empty($json)) { + return; + } + + error_log('ExtNotifConn: Portfolio item edited. Status'.((int) $json['status'])); + } +} diff --git a/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemVisibilityHookObserver.php b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemVisibilityHookObserver.php new file mode 100644 index 000000000..97cf7ed9e --- /dev/null +++ b/plugin/externalnotificationconnect/src/Hook/ExternalNotificationConnectPortfolioItemVisibilityHookObserver.php @@ -0,0 +1,44 @@ +getEventData()['item']; + $recipients = $event->getEventData()['recipients']; + + try { + $json = $this->doVisibilityRequest( + [ + 'content_id' => $item->getId(), + 'content_type' => 'eportfolio', + 'visibility' => $item->getVisibility(), + 'user_list' => $recipients, + ] + ); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + return; + } + + if (empty($json)) { + return; + } + + error_log('ExtNotifConn: Portfolio item visibility: ID '.$json['data']['notification_id']); + } +} diff --git a/plugin/externalnotificationconnect/src/Traits/RequestTrait.php b/plugin/externalnotificationconnect/src/Traits/RequestTrait.php new file mode 100644 index 000000000..f440e6614 --- /dev/null +++ b/plugin/externalnotificationconnect/src/Traits/RequestTrait.php @@ -0,0 +1,191 @@ +plugin->getAccessToken(); + } catch (OptimisticLockException|ORMException|Exception $e) { + throw new Exception($e->getMessage()); + } + + $options = [ + 'headers' => [ + 'Authorization' => "Bearer $token", + ], + 'json' => $json, + ]; + + $client = new Client(); + + try { + $response = $client->post( + $this->plugin->get(ExternalNotificationConnectPlugin::SETTING_NOTIFICATION_URL), + $options + ); + } catch (ClientException|ServerException $e) { + if (!$e->hasResponse()) { + throw new Exception($e->getMessage()); + } + + $response = $e->getResponse(); + } + + $json = json_decode((string) $response->getBody(), true); + + if (isset($json['status']) && 500 === $json['status']) { + throw new Exception($json['message']); + } + + if (isset($json['validation_errors']) && $json['validation_errors']) { + $messageError = implode( + '
    ', + array_column($json['errors'], 'message') + ); + + throw new Exception($messageError); + } + + return $json; + } + + /** + * @throws Exception + */ + protected function doEditRequest(array $json): array + { + try { + $token = $this->plugin->getAccessToken(); + } catch (OptimisticLockException|ORMException|Exception $e) { + throw new Exception($e->getMessage()); + } + + $url = $this->plugin->get(ExternalNotificationConnectPlugin::SETTING_NOTIFICATION_URL) + .'/'.$json['content_id'].'/'.$json['content_type']; + + $options = [ + 'headers' => [ + 'Authorization' => "Bearer $token", + ], + 'json' => $json, + ]; + + $client = new Client(); + + try { + $response = $client->post($url, $options); + } catch (ClientException|ServerException $e) { + if (!$e->hasResponse()) { + throw new Exception($e->getMessage()); + } + + $response = $e->getResponse(); + } + + $json = json_decode((string) $response->getBody(), true); + + if (isset($json['status']) && 500 === $json['status']) { + throw new Exception($json['message']); + } + + return $json; + } + + /** + * @throws Exception + */ + protected function doVisibilityRequest(array $data) + { + try { + $token = $this->plugin->getAccessToken(); + } catch (OptimisticLockException|ORMException|Exception $e) { + throw new Exception($e->getMessage()); + } + + $options = [ + 'headers' => [ + 'Authorization' => "Bearer $token", + ], + 'json' => $data, + ]; + + $client = new Client(); + + try { + $response = $client->post( + $this->plugin->get(ExternalNotificationConnectPlugin::SETTING_NOTIFICATION_URL).'/visibility', + $options + ); + } catch (ClientException|ServerException $e) { + if (!$e->hasResponse()) { + throw new Exception($e->getMessage()); + } + + $response = $e->getResponse(); + } + + $json = json_decode((string) $response->getBody(), true); + + if (isset($json['status']) && 500 === $json['status']) { + throw new Exception($json['message']); + } + + return $json; + } + + /** + * @throws Exception + */ + protected function doDeleteRequest(int $contentId, string $contentType): array + { + try { + $token = $this->plugin->getAccessToken(); + } catch (OptimisticLockException|ORMException|Exception $e) { + throw new Exception($e->getMessage()); + } + + $url = $this->plugin->get(ExternalNotificationConnectPlugin::SETTING_NOTIFICATION_URL)."/$contentId/$contentType"; + + $options = [ + 'headers' => [ + 'Authorization' => "Bearer $token", + ], + ]; + + $client = new Client(); + + try { + $response = $client->delete($url, $options); + } catch (ClientException|ServerException $e) { + if (!$e->hasResponse()) { + throw new Exception($e->getMessage()); + } + + $response = $e->getResponse(); + } + + $json = json_decode((string) $response->getBody(), true); + + if (isset($json['status']) && 500 === $json['status']) { + throw new Exception($json['message']); + } + + return $json; + } +} diff --git a/plugin/externalnotificationconnect/uninstall.php b/plugin/externalnotificationconnect/uninstall.php new file mode 100644 index 000000000..d16927ded --- /dev/null +++ b/plugin/externalnotificationconnect/uninstall.php @@ -0,0 +1,5 @@ +uninstall(); diff --git a/plugin/extramenufromwebservice/LICENSE.txt b/plugin/extramenufromwebservice/LICENSE.txt new file mode 100644 index 000000000..9e419e042 --- /dev/null +++ b/plugin/extramenufromwebservice/LICENSE.txt @@ -0,0 +1,674 @@ +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. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/plugin/extramenufromwebservice/README.md b/plugin/extramenufromwebservice/README.md new file mode 100644 index 000000000..eddfbff1c --- /dev/null +++ b/plugin/extramenufromwebservice/README.md @@ -0,0 +1,10 @@ +Extra menu from webservice plugin +=============================== +This plugin adds a little menu with data provided by external webservice. + +# Installation + +To install, enable the plugin, then go to "Regions" in the administration panel +and add the region "header left logo" to the plugin. Save. +The plugin should appear as a menu in the upper-left side of the Chamilo portal header. +This plugin requires the php-curl extension to be installed and enabled (a standard Chamilo requirement anyway). diff --git a/plugin/extramenufromwebservice/config.php b/plugin/extramenufromwebservice/config.php new file mode 100644 index 000000000..46e612525 --- /dev/null +++ b/plugin/extramenufromwebservice/config.php @@ -0,0 +1,4 @@ +get('tool_enable'); + // If the configuration option 'tool_enable' is disabled, doesn't show the menu + if ($pluginEnabled === 'true') { + $menuContent = ""; + $userId = api_get_user_id(); + $userData = $originalUserInfo = api_get_user_info( + api_get_user_id(), + false, + false, + false, + false, + false, + true + ); + $pluginPath = api_get_path(WEB_PLUGIN_PATH).'extramenufromwebservice/resources/'; + //Check if the token is in session, if not get a new token and write in session + if ( + Session::has('extramenufromwebservice_plugin_token') && + Session::has('extramenufromwebservice_plugin_token_start') + ) { + //if no session lifetime exists, set 1 day + $pluginSessionTimeout = !empty((int) $extraMenuFromWebservice->get('session_timeout')) ? + $extraMenuFromWebservice->get('session_timeout') : + 86400; + + $tokenStartTime = new DateTime(Session::read('extramenufromwebservice_plugin_token_start')); + + // If token is expired, get other new token + if ($extraMenuFromWebservice::tokenIsExpired($tokenStartTime->getTimestamp(), $pluginSessionTimeout)) { + $loginToken = $extraMenuFromWebservice->getToken(); + Session::write('extramenufromwebservice_plugin_token', $loginToken); + $now = api_get_utc_datetime(); + Session::write('extramenufromwebservice_plugin_token_start', $now); + } + } else { + $loginToken = $extraMenuFromWebservice->getToken(); + if (!empty($loginToken)) { + Session::write('extramenufromwebservice_plugin_token', $loginToken); + $now = api_get_utc_datetime(); + Session::write('extramenufromwebservice_plugin_token_start', $now); + } + } + + $isMobile = api_is_browser_mobile(); + $menuResponse = $extraMenuFromWebservice->getMenu( + Session::read('extramenufromwebservice_plugin_token'), + $userData['email'], + $isMobile + ); + if (!empty($menuResponse)) { + $menuContent = $menuResponse; + $fh = ''; + $fh .= ''; + if (!empty($extraMenuFromWebservice->get('list_css_imports'))) { + $cssListToImport = $extraMenuFromWebservice->getImports( + $extraMenuFromWebservice->get('list_css_imports') + ); + } + if (!empty($extraMenuFromWebservice->get('list_fonts_imports'))) { + $fontListToImport = $extraMenuFromWebservice->getImports( + $extraMenuFromWebservice->get('list_fonts_imports') + ); + } + $fh .= '
    '; + $fh .= ''; + $fh .= ''; + $fh .= ''; + $fh .= '
    '; + + echo $fh; + } + } +} diff --git a/plugin/extramenufromwebservice/install.php b/plugin/extramenufromwebservice/install.php new file mode 100644 index 000000000..550dfcbf6 --- /dev/null +++ b/plugin/extramenufromwebservice/install.php @@ -0,0 +1,7 @@ +install(); diff --git a/plugin/extramenufromwebservice/lang/english.php b/plugin/extramenufromwebservice/lang/english.php new file mode 100644 index 000000000..961a29249 --- /dev/null +++ b/plugin/extramenufromwebservice/lang/english.php @@ -0,0 +1,15 @@ + 'boolean', + 'authentication_url' => 'text', + 'authentication_email' => 'text', + 'authentication_password' => 'text', + 'normal_menu_url' => 'text', + 'mobile_menu_url' => 'text', + 'session_timeout' => 'text', + 'list_css_imports' => 'text', + 'list_fonts_imports' => 'text', + ]; + + parent::__construct( + '0.1', + 'Borja Sanchez', + $settings + ); + } + + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + public function install() + { + return true; + } + + public function uninstall() + { + $settings = [ + 'tool_enable', + 'authentication_url', + 'authentication_email', + 'authentication_password', + 'normal_menu_url', + 'mobile_menu_url', + 'username_parameter', + 'session_timeout', + 'list_css_imports' => 'text', + 'list_fonts_imports' => 'text', + ]; + + $tableSettings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); + $urlId = api_get_current_access_url_id(); + + foreach ($settings as $variable) { + $sql = "DELETE FROM $tableSettings WHERE variable = '$variable' AND access_url = $urlId"; + Database::query($sql); + } + } + + /** + * Get a token through the WS indicated in plugin configuration. + */ + public function getToken() + { + $response = []; + $authenticationUrl = (string) $this->get('authentication_url'); + $authenticationEmail = (string) $this->get('authentication_email'); + $authenticationPassword = (string) $this->get('authentication_password'); + + if (!empty($authenticationUrl) && !empty($authenticationEmail) && !empty($authenticationPassword)) { + $curl = curl_init(); + + curl_setopt_array($curl, [ + CURLOPT_URL => $authenticationUrl, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 5, + CURLOPT_NOSIGNAL => 1, + CURLOPT_SSL_VERIFYHOST => 0, + CURLOPT_SSL_VERIFYPEER => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => '{ + "email": "'.$authenticationEmail.'", + "password": "'.$authenticationPassword.'" + }', + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + ], + ]); + + $curlResponse = curl_exec($curl); + curl_close($curl); + if (false !== $curlResponse) { + $curlResponse = json_decode($curlResponse, true); + + if (isset($curlResponse['data']['data']['token'])) { + $response = $curlResponse['data']['data']['token']; + } + } + } + + return $response; + } + + /** + * Get the menu from the WS indicated in plugin configuration. + * */ + public function getMenu( + string $token, + string $userEmail, + bool $isMobile = false + ): array { + $response = []; + $menuUrl = $isMobile ? (string) $this->get('mobile_menu_url') : (string) $this->get('normal_menu_url'); + if (!empty($menuUrl) && !empty($token) && !empty($userEmail)) { + $menuUrl = substr($menuUrl, -1) === '/' ? $menuUrl : $menuUrl.'/'; + $curl = curl_init(); + + curl_setopt_array($curl, [ + CURLOPT_URL => $menuUrl.$userEmail, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 5, + CURLOPT_NOSIGNAL => 1, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'GET', + CURLOPT_HTTPHEADER => [ + "Authorization: Bearer $token", + ], + ]); + + $curlResponse = curl_exec($curl); + if (false !== $curlResponse) { + $curlResponse = json_decode($curlResponse, true); + if (isset($curlResponse['data']['data']['html']['data'])) { + $response['html'] = $curlResponse['data']['data']['html']['data']; + } + if (isset($curlResponse['data']['data']['css']['data'])) { + $response['css'] = $curlResponse['data']['data']['css']['data']; + } + if (isset($curlResponse['data']['data']['js']['data'])) { + $response['js'] = $curlResponse['data']['data']['js']['data']; + } + } + curl_close($curl); + } + + return $response; + } + + /** + * Checks if the login token is expired. + */ + public static function tokenIsExpired(int $tokenStartTime, int $pluginSessionTimeout): bool + { + $now = api_get_utc_datetime(null, false, true)->getTimestamp(); + + return ($now - $tokenStartTime) > $pluginSessionTimeout; + } + + /** + * Get the list of CSS or fonts indicated in plugin configuration. + */ + public static function getImports(string $list = '') + { + $importsArray = []; + + if (!empty($list)) { + $importsArray = explode(";", $list); + } + + return $importsArray; + } +} diff --git a/plugin/extramenufromwebservice/plugin.php b/plugin/extramenufromwebservice/plugin.php new file mode 100644 index 000000000..856a8271d --- /dev/null +++ b/plugin/extramenufromwebservice/plugin.php @@ -0,0 +1,5 @@ +get_info(); diff --git a/plugin/extramenufromwebservice/resources/css/extramenufromwebservice.css b/plugin/extramenufromwebservice/resources/css/extramenufromwebservice.css new file mode 100644 index 000000000..3b8a4e7cd --- /dev/null +++ b/plugin/extramenufromwebservice/resources/css/extramenufromwebservice.css @@ -0,0 +1,108 @@ +.extra-menu-from-webservice { + position: relative; +} + +.nav-from-webservice { + display: none; + position: absolute; + width: 250px; + top: 90px; + z-index: 10; +} + +@media (min-width: 176px) { + .nav-from-webservice { + top: 110px; + } +} + +@media (min-width: 490px) { + .nav-from-webservice { + top: 131px; + } +} + +@media (min-width: 576px) { + .nav-from-webservice { + top: 131px; + } +} + +@media (min-width: 768px) { + .nav-from-webservice { + top: 129px; + } +} + +@media (min-width: 992px) { + .nav-from-webservice { + top: 82px; + } +} + +@media (min-width: 1200px) { + .nav-from-webservice { + top: 89px; + } +} + +.menu-btn { + position: sticky; + width: 30px; + height: 26px; + cursor: pointer; + z-index: 12; + margin-top: 45px; + margin-left: 30px; +} + +.menu-btn > span, +.menu-btn > span::before, +.menu-btn > span::after { + display: block; + position: absolute; + width: 100%; + height: 3px; + background-color: #252323; + transition-duration: 0.25s; +} + +.menu-btn > span::before { + content: ""; + top: -8px; +} + +.menu-btn > span::after { + content: ""; + top: 8px; +} + +#menu-toggle { + opacity: 0; + position: fixed; + top: 20px; + left: 345px; +} + +#menu-toggle:checked + .menu-btn > span { + transform: rotate(45deg); +} + +#menu-toggle:checked + .menu-btn > span::before { + top: 0; + transform: rotate(0deg); +} + +#menu-toggle:checked + .menu-btn > span::after { + top: 0; + transform: rotate(90deg); +} +#date_alt_text { + z-index: 0; !important; +} +#frm_tag_list_text { + z-index: 0; !important; +} +.input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { + z-index: 0; !important; +} diff --git a/plugin/extramenufromwebservice/resources/js/extramenufromwebservice.js b/plugin/extramenufromwebservice/resources/js/extramenufromwebservice.js new file mode 100644 index 000000000..17f801d0d --- /dev/null +++ b/plugin/extramenufromwebservice/resources/js/extramenufromwebservice.js @@ -0,0 +1,8 @@ +$(document).ready(function () { + $('#menu-toggle').click(function() { + $("#nav-from-webservice").toggle("slow"); + $("#nav-from-webservice").css("z-index", 15); + }); +// Uncomment following line to set the menu open by default +// $('#menu-toggle').click(); +}); diff --git a/plugin/extramenufromwebservice/uninstall.php b/plugin/extramenufromwebservice/uninstall.php new file mode 100644 index 000000000..a96e3423a --- /dev/null +++ b/plugin/extramenufromwebservice/uninstall.php @@ -0,0 +1,5 @@ +uninstall(); diff --git a/plugin/follow_buttons/README.md b/plugin/follow_buttons/README.md new file mode 100644 index 000000000..57a460093 --- /dev/null +++ b/plugin/follow_buttons/README.md @@ -0,0 +1,6 @@ +Follow buttons (or AddThis icons) +=== + +Edit the main/plugin/follow_buttons/index.php file in order to add your settings. + +It can slow down your website because it will load a javascript file from the addthis.com server \ No newline at end of file diff --git a/plugin/follow_buttons/index.php b/plugin/follow_buttons/index.php new file mode 100644 index 000000000..61c30b777 --- /dev/null +++ b/plugin/follow_buttons/index.php @@ -0,0 +1,20 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +/* Plugin config */ + +//the plugin title +$plugin_info['title'] = 'Follow icons'; +//the comments that go with the plugin +$plugin_info['comment'] = "Add social icons (implemented using addthis.com)"; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Julio Montoya'; + +$plugin_info['templates'] = ['template.tpl']; + +//For bigger icons change this value to addthis_32x32_style +$plugin_info['icon_class'] = ''; + +//To use vertical alignment change this value to +$plugin_info['position'] = 'addthis_default_style'; //addthis_vertical_style diff --git a/plugin/follow_buttons/template.tpl b/plugin/follow_buttons/template.tpl new file mode 100644 index 000000000..cbf43fc13 --- /dev/null +++ b/plugin/follow_buttons/template.tpl @@ -0,0 +1,35 @@ +
    + +{% if follow_buttons.message is not empty %} +

    {{ follow_buttons.message }}

    +{% endif %} + +
    + + +
    \ No newline at end of file diff --git a/plugin/formLogin_hide_unhide/README.md b/plugin/formLogin_hide_unhide/README.md new file mode 100644 index 000000000..7493ed5bf --- /dev/null +++ b/plugin/formLogin_hide_unhide/README.md @@ -0,0 +1,7 @@ +Hide/Show login form plugin +=== + +If you use another way of authentication than local, you may want to hide the Login/Password default Form to avoid user mistakes. +This plugin replaces the Login/Password form with a text that shows the Login/Password form if you click on it. + +This plugin has been done to be added in the login_top region. \ No newline at end of file diff --git a/plugin/formLogin_hide_unhide/css.css b/plugin/formLogin_hide_unhide/css.css new file mode 100644 index 000000000..d6b83ebe7 --- /dev/null +++ b/plugin/formLogin_hide_unhide/css.css @@ -0,0 +1,3 @@ +.formLogin_hide_unhide_div { + cursor:pointer; +} diff --git a/plugin/formLogin_hide_unhide/index.php b/plugin/formLogin_hide_unhide/index.php new file mode 100644 index 000000000..9fa1f0926 --- /dev/null +++ b/plugin/formLogin_hide_unhide/index.php @@ -0,0 +1,13 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +/** + * Plugin details (must be present). + */ + +//the plugin title +$plugin_info['title'] = 'Hide/Unhide the Login/Password default form'; + +//the comments that go with the plugin +$plugin_info['comment'] = "If you use another way of authentication than local, you may want to hide the Login/Password default Form to avoid users mistakes. This plugin replace the Login/Password form with a text that unhide the Login/Password form if you click on it."; +//the plugin version +$plugin_info['version'] = '1.0'; +//the plugin author +$plugin_info['author'] = 'Hubert Borderiou'; + +//the plugin configuration +$form = new FormValidator('add_cas_button_form'); +$form->addElement('text', 'label', 'Text label', ''); +//get default value +$tab_default_formLogin_hide_unhide_label = api_get_setting('formLogin_hide_unhide_label'); +$defaults = []; +if ($tab_default_formLogin_hide_unhide_label) { + $defaults['label'] = $tab_default_formLogin_hide_unhide_label['formLogin_hide_unhide']; +} + +$form->setDefaults($defaults); +//display form +$plugin_info['settings_form'] = $form; + +//set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; diff --git a/plugin/formLogin_hide_unhide/template.tpl b/plugin/formLogin_hide_unhide/template.tpl new file mode 100644 index 000000000..d79c0ca07 --- /dev/null +++ b/plugin/formLogin_hide_unhide/template.tpl @@ -0,0 +1,11 @@ +{% if formLogin_hide_unhide.show_message %} + + + +{% endif %} diff --git a/plugin/google_maps/README.md b/plugin/google_maps/README.md new file mode 100644 index 000000000..bdf1c3012 --- /dev/null +++ b/plugin/google_maps/README.md @@ -0,0 +1,4 @@ +Google Maps Plugin +=================== + +Activa la funcionalidad de mostrar mapas usando el api de google maps diff --git a/plugin/google_maps/config.php b/plugin/google_maps/config.php new file mode 100644 index 000000000..0f06c6dc9 --- /dev/null +++ b/plugin/google_maps/config.php @@ -0,0 +1,10 @@ + + * + * @package chamilo.plugin.google_maps + */ +define('TABLE_GOOGLE_MAPS', 'plugin_google_maps'); diff --git a/plugin/google_maps/index.php b/plugin/google_maps/index.php new file mode 100644 index 000000000..00635519c --- /dev/null +++ b/plugin/google_maps/index.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.google_maps + */ +require_once __DIR__.'/config.php'; + +$googleMapsPlugin = GoogleMapsPlugin::create(); diff --git a/plugin/google_maps/install.php b/plugin/google_maps/install.php new file mode 100644 index 000000000..678729ca6 --- /dev/null +++ b/plugin/google_maps/install.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.google_maps + */ +require_once __DIR__.'/config.php'; + +GoogleMapsPlugin::create()->install(); diff --git a/plugin/google_maps/lang/english.php b/plugin/google_maps/lang/english.php new file mode 100644 index 000000000..3172443a1 --- /dev/null +++ b/plugin/google_maps/lang/english.php @@ -0,0 +1,13 @@ +
    + +

    +Follow the link below to see the map : ".api_get_path(WEB_PLUGIN_PATH)."google_maps/src/map_coordinates.php"; +$strings['enable_api'] = "Enable API"; +$strings['api_key'] = "Api Key"; +$strings['extra_field_name'] = "Extra field name"; +$strings['UsersCoordinatesMap'] = "Users location Map"; diff --git a/plugin/google_maps/lang/french.php b/plugin/google_maps/lang/french.php new file mode 100644 index 000000000..0fb12d582 --- /dev/null +++ b/plugin/google_maps/lang/french.php @@ -0,0 +1,13 @@ +
    + +

    +Suivez le lien pour voir la carte : ".api_get_path(WEB_PLUGIN_PATH)."google_maps/src/map_coordinates.php"; +$strings['enable_api'] = "Enable API"; +$strings['api_key'] = "Api Key"; +$strings['extra_field_name'] = "Nom de champ supplémentaire"; +$strings['UsersCoordinatesMap'] = "Carte des emplacements des utilisateurs"; diff --git a/plugin/google_maps/lang/spanish.php b/plugin/google_maps/lang/spanish.php new file mode 100644 index 000000000..790a53a54 --- /dev/null +++ b/plugin/google_maps/lang/spanish.php @@ -0,0 +1,13 @@ +
    + +

    +siga el siguiente link para ver el mapa : ".api_get_path(WEB_PLUGIN_PATH)."google_maps/src/map_coordinates.php"; +$strings['enable_api'] = "Activar API"; +$strings['api_key'] = "Api Key"; +$strings['extra_field_name'] = "Nombre del campo extra"; +$strings['UsersCoordinatesMap'] = "Mapa de localización de Usuarios"; diff --git a/plugin/google_maps/plugin.php b/plugin/google_maps/plugin.php new file mode 100644 index 000000000..52ed6874f --- /dev/null +++ b/plugin/google_maps/plugin.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.google_maps + */ +require_once __DIR__.'/config.php'; + +$plugin_info = GoogleMapsPlugin::create()->get_info(); diff --git a/plugin/google_maps/src/GoogleMapsPlugin.php b/plugin/google_maps/src/GoogleMapsPlugin.php new file mode 100644 index 000000000..2d11d57cf --- /dev/null +++ b/plugin/google_maps/src/GoogleMapsPlugin.php @@ -0,0 +1,70 @@ + + * + * @package chamilo.plugin.google_maps + */ +class GoogleMapsPlugin extends Plugin +{ + public $javascriptIncluded; + + /** + * Class constructor. + */ + protected function __construct() + { + $parameters = [ + 'enable_api' => 'boolean', + 'api_key' => 'text', + 'extra_field_name' => 'text', + ]; + + $this->javascriptIncluded = false; + + parent::__construct('1.0', 'José Loguercio Silva', $parameters); + } + + /** + * Get the plugin Name. + * + * @return string + */ + public function get_name() + { + return 'google_maps'; + } + + /** + * Instance the plugin. + * + * @staticvar null $result + * + * @return GoogleMapsPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Install the plugin. + */ + public function install() + { + return true; + } + + /** + * Uninstall the plugin. + */ + public function uninstall() + { + return true; + } +} diff --git a/plugin/google_maps/src/map_coordinates.php b/plugin/google_maps/src/map_coordinates.php new file mode 100644 index 000000000..0222aaedd --- /dev/null +++ b/plugin/google_maps/src/map_coordinates.php @@ -0,0 +1,68 @@ + + * + * @package chamilo.plugin.google_maps + */ +$cidReset = true; + +require_once __DIR__.'/../../../main/inc/global.inc.php'; + +api_protect_admin_script(); + +$plugin = GoogleMapsPlugin::create(); + +$apiIsEnable = $plugin->get('enable_api') === 'true'; +$extraFieldName = $plugin->get('extra_field_name'); + +$extraFieldName = array_map('trim', explode(',', $extraFieldName)); + +if ($apiIsEnable) { + $gmapsApiKey = $plugin->get('api_key'); + $htmlHeadXtra[] = ''; +} + +$em = Database::getManager(); +$extraField = $em->getRepository('ChamiloCoreBundle:ExtraField'); + +$extraFieldNames = []; + +foreach ($extraFieldName as $field) { + $extraFieldNames[] = $extraField->findOneBy(['variable' => $field]); +} + +$extraFieldValues = []; + +foreach ($extraFieldNames as $index => $fieldName) { + if ($fieldName) { + $extraFieldRepo = $em->getRepository('ChamiloCoreBundle:ExtraFieldValues'); + $extraFieldValues[] = $extraFieldRepo->findBy(['field' => $fieldName->getId()]); + } +} + +$templateName = $plugin->get_lang('UsersCoordinatesMap'); + +$tpl = new Template($templateName); + +$formattedExtraFieldValues = []; + +foreach ($extraFieldValues as $index => $extra) { + foreach ($extra as $yandex => $field) { + $thisUserExtraField = api_get_user_info($field->getItemId()); + $formattedExtraFieldValues[$index][$yandex]['address'] = $field->getValue(); + $formattedExtraFieldValues[$index][$yandex]['user_complete_name'] = $thisUserExtraField['complete_name']; + } +} + +$tpl->assign('extra_field_values_formatted', $formattedExtraFieldValues); +$tpl->assign('extra_field_values', $extraFieldValues); + +$content = $tpl->fetch('google_maps/view/map_coordinates.tpl'); + +$tpl->assign('header', $templateName); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/google_maps/uninstall.php b/plugin/google_maps/uninstall.php new file mode 100644 index 000000000..366e035ba --- /dev/null +++ b/plugin/google_maps/uninstall.php @@ -0,0 +1,12 @@ + + * + * @package chamilo.plugin.google_maps + */ +require_once __DIR__.'/config.php'; + +GoogleMapsPlugin::create()->uninstall(); diff --git a/plugin/google_maps/view/map_coordinates.tpl b/plugin/google_maps/view/map_coordinates.tpl new file mode 100644 index 000000000..c4064190b --- /dev/null +++ b/plugin/google_maps/view/map_coordinates.tpl @@ -0,0 +1,105 @@ +
    + +
    + \ No newline at end of file diff --git a/plugin/google_meet/README.md b/plugin/google_meet/README.md new file mode 100644 index 000000000..ffe69462b --- /dev/null +++ b/plugin/google_meet/README.md @@ -0,0 +1,18 @@ +Google Meet plugin +============================ +This plugin allows you to have videoconference rooms in each course using Google Meet. +This is only a light integration, meaning you will still need to go to Google Meet and +have a Google account in order to create a meeting *and* students will need to be +connected to their Google account to connect. However, we believe there is still value +in making this plugin available for our community at large. + +# Changelog + +## Version 1.1 - 2020-11-29 + +Add sessions management + +## Version 1.0 - 2020-07-09 + +Initial release. Includes the possibility to create meetings manually, through +a link to https://meet.google.com/ \ No newline at end of file diff --git a/plugin/google_meet/config.php b/plugin/google_meet/config.php new file mode 100644 index 000000000..52bded383 --- /dev/null +++ b/plugin/google_meet/config.php @@ -0,0 +1,10 @@ + + * + * @package chamilo.plugin.google_meet + */ +require_once __DIR__.'/../../main/inc/global.inc.php'; +require_once 'src/google_meet_plugin.class.php'; diff --git a/plugin/google_meet/install.php b/plugin/google_meet/install.php new file mode 100644 index 000000000..922bcf4fe --- /dev/null +++ b/plugin/google_meet/install.php @@ -0,0 +1,10 @@ +install(); diff --git a/plugin/google_meet/lang/english.php b/plugin/google_meet/lang/english.php new file mode 100644 index 000000000..42a17c6db --- /dev/null +++ b/plugin/google_meet/lang/english.php @@ -0,0 +1,22 @@ +click here to create one, then save the URL and put it in this field.'; +$strings['AccessMeeting'] = 'Access the meeting'; +$strings['MeetingDescription'] = 'Description'; +$strings['MeetingDescriptionHelp'] = '(Optional) Add a short description for the meeting.'; +$strings['MeetColor'] = 'Color of Meet'; +$strings['MeetColorHelp'] = '(Optional)'; diff --git a/plugin/google_meet/lang/french.php b/plugin/google_meet/lang/french.php new file mode 100644 index 000000000..92b914362 --- /dev/null +++ b/plugin/google_meet/lang/french.php @@ -0,0 +1,22 @@ +cliquez ici pour en créer une, puis copiez son URL et collez-la dans ce champs.'; +$strings['AccessMeeting'] = 'Accéder à la réunion'; +$strings['MeetingDescription'] = 'Description'; +$strings['MeetingDescriptionHelp'] = '(Optionnel) Ajouter une brève description.'; +$strings['MeetColor'] = 'Couleur de la réunion'; +$strings['MeetColorHelp'] = '(Optionnel)'; diff --git a/plugin/google_meet/lang/spanish.php b/plugin/google_meet/lang/spanish.php new file mode 100644 index 000000000..90d0f2e25 --- /dev/null +++ b/plugin/google_meet/lang/spanish.php @@ -0,0 +1,22 @@ +haga clic aquí para crear una, luego guarde la URL y colóquela en el campo arriba.'; +$strings['AccessMeeting'] = 'Acceder a la reunión'; +$strings['MeetingDescription'] = 'Descripción'; +$strings['MeetingDescriptionHelp'] = '(Opcional) Añada una breve descripción sobre la reunión.'; +$strings['MeetColor'] = 'Color de Meet'; +$strings['MeetColorHelp'] = '(Opcional)'; diff --git a/plugin/google_meet/meets.php b/plugin/google_meet/meets.php new file mode 100644 index 000000000..00920f6f6 --- /dev/null +++ b/plugin/google_meet/meets.php @@ -0,0 +1,226 @@ +'; + +$plugin = GoogleMeetPlugin::create(); + +$userId = api_get_user_id(); +$tool_name = $plugin->get_lang('tool_title'); +$tpl = new Template($tool_name); + +$isAdmin = api_is_platform_admin(); +$isTeacher = api_is_teacher(); +$message = null; + +$action = isset($_GET['action']) ? $_GET['action'] : null; +$enable = $plugin->get('google_meet_enabled') == 'true'; +$actionLinks = ''; + +if ($enable) { + if ($isAdmin || $isTeacher) { + if ($action) { + switch ($action) { + case 'delete': + $idMeet = isset($_GET['id_meet']) ? $_GET['id_meet'] : null; + $res = $plugin->deleteMeet($idMeet); + if ($res) { + $url = api_get_path(WEB_PLUGIN_PATH).'google_meet/start.php?'.api_get_cidreq(); + header('Location: '.$url); + } + + break; + case 'add': + $actionLinks .= Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'google_meet/start.php?'.api_get_cidreq() + ); + //create form + $form = new FormValidator( + 'add_meet', + 'post', + api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&'.api_get_cidreq() + ); + + $form->addHeader(get_lang('AddMeet')); + $form->addText( + 'meet_name', + [ + $plugin->get_lang('MeetName'), + $plugin->get_lang('MeetNameHelp'), + ], + true, + [ + 'title' => $plugin->get_lang('MeetNameHelp'), + ] + ); + + $form->addText( + 'meet_url', + [ + $plugin->get_lang('GoogleMeetURL'), + sprintf($plugin->get_lang('GoogleMeetURLHelp'), GoogleMeetPlugin::GOOGLE_MEET_URL), + ], + true, + [ + 'title' => sprintf($plugin->get_lang('GoogleMeetURLHelp'), GoogleMeetPlugin::GOOGLE_MEET_URL), + ] + ); + + try { + $form->addElement( + 'color', + 'meet_color', + [ + $plugin->get_lang('MeetColor'), + $plugin->get_lang('MeetColorHelp'), + ], + [ + 'value' => '#1CC88A', + ] + ); + } catch (HTML_QuickForm_Error $e) { + echo $e; + } + + $form->addHtmlEditor( + 'meet_description', + [ + $plugin->get_lang('MeetingDescription'), + $plugin->get_lang('MeetingDescriptionHelp'), + ], + false, + false, + [ + 'ToolbarSet' => 'Minimal', + ] + ); + + $form->addHidden('type_meet', 1); + + $form->addButtonSave($plugin->get_lang('Add')); + + if (!empty($defaults)) { + $form->setDefaults($defaults); + } + + try { + if ($form->validate()) { + $values = $form->exportValues(); + $res = $plugin->saveMeet($values); + if ($res) { + $url = api_get_path(WEB_PLUGIN_PATH).'google_meet/start.php?'.api_get_cidreq(); + header('Location: '.$url); + } + } + } catch (HTML_QuickForm_Error $e) { + echo $e; + } + + $tpl->assign('form_room', $form->returnForm()); + + break; + case 'edit': + $actionLinks .= Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'google_meet/start.php?'.api_get_cidreq() + ); + + $idMeet = isset($_GET['id_meet']) ? (int) $_GET['id_meet'] : 0; + $dataMeet = $plugin->getMeet($idMeet); + + //create form + $form = new FormValidator( + 'edit_meet', + 'post', + api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&'.api_get_cidreq() + ); + + $form->addHeader(get_lang('EditMeet')); + + $form->addText( + 'meet_name', + [ + $plugin->get_lang('MeetName'), + $plugin->get_lang('MeetNameHelp'), + ], + true, + [ + 'title' => $plugin->get_lang('MeetNameHelp'), + ] + ); + + $form->addText( + 'meet_url', + [ + $plugin->get_lang('GoogleMeetURL'), + sprintf($plugin->get_lang('GoogleMeetURLHelp'), GoogleMeetPlugin::GOOGLE_MEET_URL), + ], + true, + [ + 'title' => sprintf($plugin->get_lang('GoogleMeetURLHelp'), GoogleMeetPlugin::GOOGLE_MEET_URL), + ] + ); + $form->addElement( + 'color', + 'meet_color', + [ + $plugin->get_lang('MeetColor'), + $plugin->get_lang('MeetColorHelp'), + ] + ); + $form->addHtmlEditor( + 'meet_description', + [ + $plugin->get_lang('MeetingDescription'), + $plugin->get_lang('MeetingDescriptionHelp'), + ], + false, + false, + [ + 'ToolbarSet' => 'Minimal', + ] + ); + + $form->addHidden('id', $idMeet); + $form->addButtonSave($plugin->get_lang('Save')); + + $form->setDefaults($dataMeet); + + if ($form->validate()) { + $values = $form->exportValues(); + $res = $plugin->updateMeet($values); + + if ($res) { + $url = api_get_path(WEB_PLUGIN_PATH).'google_meet/start.php?'.api_get_cidreq(); + header('Location: '.$url); + } + } + + $tpl->assign('form_room', $form->returnForm()); + + break; + } + } + } +} + +if ($isAdmin || $isTeacher) { + $tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) + ); +} + +$tpl->assign('message', $message); +$content = $tpl->fetch('google_meet/view/meets.tpl'); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/google_meet/plugin.php b/plugin/google_meet/plugin.php new file mode 100644 index 000000000..c1fac9daf --- /dev/null +++ b/plugin/google_meet/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/google_meet/resources/css/style.css b/plugin/google_meet/resources/css/style.css new file mode 100644 index 000000000..02db1506c --- /dev/null +++ b/plugin/google_meet/resources/css/style.css @@ -0,0 +1,148 @@ +.plugin_logo{ + text-align: center; + padding-top: 1rem; + padding-bottom: 1rem; +} +.plugin_logo .img-responsive{ + margin: auto; +} +.tools{ + margin-bottom: 2rem; + margin-top: 1rem; +} +.card{ + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + border: 1px solid rgba(0,0,0,.05); + border-radius: .375rem; + background-color: #fff; + background-clip: border-box; +} +.card-meet{ + margin-bottom: 30px; + border: 0; + box-shadow: 0 0 2rem 0 rgba(136,152,170,.15); +} +.card-meet:hover{ + border-left: .25rem solid #cdcdcd !important; +} +.card-meet .card-body { + padding: 1rem 1.5rem; +} +.row-meet { + display: block; +} + +.description{ + padding-left: 1rem; + padding-right: 1rem; + padding-top: 2rem; + font-size: 12px; + color: #666666; + display: grid; +} +.description .title{ + font-size: 1.45rem; + font-weight: 600!important; + color: #32325d; + overflow:hidden; + white-space:nowrap; + text-overflow: ellipsis; + width: 100%; + display: inline-block; +} +.description .text{ + display: grid; + padding-bottom: 1rem; +} +.rounded-circle { + border-radius: 50%!important; +} +.shadow { + box-shadow: 0 0 2rem 0 rgba(136,152,170,.15)!important; +} +.icon { + width: 5rem; + height: 5rem; +} +.icon-shape { + display: inline-flex; + padding: 12px; + text-align: center; + border-radius: 50%; + align-items: center; + justify-content: center; + position: absolute; + right: 40%; + top: -20px; +} +.icon-shape .fa{ + font-size: 20px; +} + +.text-white { + color: #fff!important; +} +.btn-meet{ + color: #fff; + border-color: #5e72e4; + background-color: #5e72e4; + box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08); +} +.btn-meet:hover { + color: #fff; + border-color: #5e72e4; + background-color: #5e72e4; +} +.btn:hover { + transform: translateY(-1px); + box-shadow: 0 7px 14px rgba(50,50,93,.1), 0 3px 6px rgba(0,0,0,.08); +} +.float-right { + position: absolute; + top: 5px; + right: 5px; +} +.btn-add-meet{ + color: #fff; + border-color: #1cc88a; + background-color: #1cc88a; + font-weight: bold; +} +.btn-add-meet:hover { + color: #fff; + border-color: #17a673; + background-color: #169b6b; +} +.meet-list{ + display: grid; + grid-template-columns: repeat(3, 1fr); +} +.meet-list .meet-item{ + margin: 10px; +} +.meet-list .meet-item .card-meet{ + min-height: 220px; +} +@media (min-width: 768px) and (max-width: 979px) { + .meet-list { + display: grid; + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 767px) { + .meet-list { + display: grid; + grid-template-columns: repeat(2, 1fr); + } +} +/* Landscape phones and down */ +@media (max-width: 480px) { + .meet-list { + display: grid; + grid-template-columns: repeat(1, 1fr); + } +} \ No newline at end of file diff --git a/plugin/google_meet/resources/img/22/google_meet.png b/plugin/google_meet/resources/img/22/google_meet.png new file mode 100644 index 0000000000000000000000000000000000000000..4d19baa2cbb61091a6c2f6966be75b4a3cdfcb60 GIT binary patch literal 5030 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 literal 0 HcmV?d00001 diff --git a/plugin/google_meet/resources/img/22/google_meet_na.png b/plugin/google_meet/resources/img/22/google_meet_na.png new file mode 100644 index 0000000000000000000000000000000000000000..5c51623e660490cefc46e6ba95b24e8e0347db81 GIT binary patch literal 3683 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 literal 0 HcmV?d00001 diff --git a/plugin/google_meet/resources/img/32/google_meet_na.png b/plugin/google_meet/resources/img/32/google_meet_na.png new file mode 100644 index 0000000000000000000000000000000000000000..4eee5a79773a709f6040f1b3b12b18b40415194d GIT binary patch literal 2357 zcmV-53Ci|~P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1e?a`QF}{LfSL2qZx8aUAe9(;M{oT~LzaBz9td ztu+!M3L-)5VyVQl?nB68wL4G07g8VHgib0U|i zkj;&Ea@f8kl*qcFO#nhTaRxT1DS#EDfIm4Fa7a;GQACntDNd?r;uMpllyWkW)66+$ z$tmYta?M(}#3Ch?TuP}$D^-RDN=-G_QfpNurNbOf%0i>(m9VEnacSN-M9j>e3r(C#v6$H&6>F zYP69uzkEXty)Nf#h+uLeh8Yl}7=U;-1VCsq%zOzkk|Af9`AR4XAp|LgjT^xb1B7t~ zk`->)eIWM|ZcfTexY2LOIfL&1K+XZW@3=icZP-57H)2N>T24Kq`Z_BWb=c3m8Lzc< z#EB8hw8$&^h_E0mW;oku3e2dsN!lxa*1CrekZ#eM_Jqi4`f-$6Xsj?$~(6ZLHzZc+mTbsL#EK3HyM0_qtxXiJaih6kQ#@SuZp!JGAl=?9R7?1=e zba_VtjfUheuj zj=O~yc(jWN!XAen30Ki0J1P?4 zf$(NlBh|U-&~PV7PnnMB~Xih~7F5l5{;5h{dQp;ZTyOFx7r z4M~fOqu^R_@ME#+;Nq;SgR3A2etR+LrZbK+5xE=c^yb;ad3&IN}Bo*6OIsX5{Zu~=whrHxt9)QBgEqpGG; zzL0TQ<-EmND^*$Zp8SR3oW8uob(#Z6U;&GeAVNVE8z`ZI7_B-f7E-hy_we^Seu-QP zxi-MaF^@7d$c`WU4}N!RQNG$Gb!Az5RQp+20RRymFf6Qkb9s004?mOjJbxaR6jwWMyS#W@ct*XJ=?= zXlZF_YHDh0Yin$5Y;A3AZf3}bii(P!o}Q(prMFVn0@9*#N@$vTd_AHrlZU6uP0d!JMQvg8b*k%9#00Cl4M??UK1szBL000Sa zNLh0L00*A{00*A|Fk}qu00007bV*G`2jmML2n07&)zdlv009+AL_t(I%axPocEcbH zMvcJ;Bz2^FH(hBPH}C&?MP6V$&dL4zUCGFZqBsJjE3np-3vmg=E#9b^YTyLad9IPi zR9SmsUNaQ(6q5)rlQYF>G|vL#23M~i9Z#3cr{Pf4538CJKJEr?c=K#2o}z3TzC_`6 zvwgP~hi{l%c-ZcO0O*#4fCz5f;pdQ2SKxdvXv&1Q{h_wbBMei}Shzpi*P#Ipe;dU3 zio%^e{|1jVT&bpDT>t)cd|rS%2|32C@M3tQTa1wRkT~R=uAUL0H`@B- zu7oNYU5+Z>xz>O#r^!~978}DkIRKftXJNo0we@E4?I4Hp>DEVNeO+0s3f1cSbgShu bOmQs#j>;r}m(>1!00000NkvXXu0mjfc6C}| literal 0 HcmV?d00001 diff --git a/plugin/google_meet/resources/img/64/google_meet.png b/plugin/google_meet/resources/img/64/google_meet.png new file mode 100644 index 0000000000000000000000000000000000000000..96a160ac25e79c029b1dc889c02223476695df89 GIT binary patch literal 2629 zcmV-L3cB@)P);M1&8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H13BpN4K~#90?VEp$TvZjvKj*$Tvp?Ey3#~$pfC$L12viViqEtvk zY@!CENR*JE0YXZt+e-K)?n5L%ySv3AL`h>JB!JdL0|BY^7l;u}5F)iEEfhf|)ksTd z+4=SEJ^u0D%+8NDZ{O_B?3m~`natex?!D)n?>+b2bMJct|KpH(lQcOx#9Q8R7`qfT z!=MXMJpeKVx{p)-{wf!5v-74;jNTD&+qx66=3^iq1GAi0?(;o(IOXG*fHATGTnzTAVsiD!_Jx2hfop z69i#bb&b&-0&ZV-1Qq`}&?^NNiSJ`_dBpc--KAD4xN-&OEWj(EB7b-RnE6{wv=U;AMHIIOyw4Df z@0?42oojyUhIOb;dVbEB!5)u#JE&r(3OHvEBJw%LCbrMEtIpI9)(0SCUOtZ*7$925 z`UC}6EMmh-36YB!oA~K$y2(yJFnSFt_xMADNUj>_`baMogBh zuD{d4mCM*tsSL7$U}9I6Htjti2nK;WJ>So0JoMWWOg@holLy_DxlP+Ua_|a7mYY=9 zPi)v>{q7>3s;db`&(2amnSdZLsBZB597zA?cP9|f!x#g|4VmgEUJXVsKxKPw;ljl2 zYctMR`VPKw>IXP^$x?Dstv|bX@J6Y~&tHF)Eq{KVf=%85i-M+%i&jFyVj%?YD1uG< z(&YKcm*q0!nM}qXKeB{}R;^~hoBh_e9*gq>TzTSY96z#zE1ucWa^Dlf^{1W1?a%!V z$@wG^ExwN+zG5!{?oN}}YGDu<;N#QTUOK*X!l_*|z8>cR6iA zEQx@H{m$m9$ZULl91^17sI}`O$&Df;5zysh-5#SM2n)5rtE~)@n#(5zIJQsm8`qyl1OM;`eHArg+7gAg!+F;>px*PxM?4-OT&b{cmfHohe6QB@~mfAv*S@7 zdGYx+HKp!LK!jNTy#yOyPm__H^b6ZunC)eam)_i;sord``@kDi1Do|cuHNw&kL-Lt z9_+|aj6`JvB5@EDh&))Hnn^}70jdwGI#ruMLX#KV`@$b6CV9&o@x<<35xHI*-~_C1 zxO&H9JiPOTnxR0n@o9<&E_5KYo~SgTea;(r!8i>)65+&bl8GOniPOX(`VI zCY}MFw53w}*461C0=XPAJPZqlVPIempFHIJx?ycKx;iBqjF01jDmZHa`R+`{rxO5x zu2pA?u2PwqgsQr}4|8}oDAKB$B<}yxNBv0j^X4c#dHFmzhMR-VHXtgzybp zE}?08KsQNf$m@wmyirYQ@Cs^PplYMh7w*a|ZpIoqau=|}7K`ae{YfNrdbB3Zh57(k zO>~mBztJ^UD1z#wT)SEef_2kCAs(Fj2qvGM*`R4;_t9H6 zy$o``v-TghSfDKn%_NA#B(#YM)opCO{q>n%Vp$rvvc&?bx)+L>o(Hyw&x~MV7wEap zg?qxn6p5K4v?0N34UE-jpK5qD;?t1%)kKv`xG*eX%ok=#dXmuj1At)jUWRgKs?%*@ zv50H;cq)km&zqhO&9N-;Vrz<9ANn;6v86HyYx(;4?>n}oNn+=1gAHE>-6+N!=H&+> z1Dlcba&)B<5mcAdO!qoHzolAEsH4^rPE7&&ZGy?Qvo&HakARe5Vk>y3sB_;7i-j

    zn)ib{3gk%Oecs@ZZ*sY)p0z02WPIZeFn$9J;5(f1+BMz!drfZ%Xoq0^Yu@0{Q8fYp zq7$50t46hKsf4SPLxis}Hkmok&w{y~Ud$e?ka4}<(zv#9i zeIy_$la$)Jq!o)T6;X9NK|jsd=Dod8q+f4BXyPL^kvOj}5f-OZ)!xqnvyz}U#zX%+ zK$F6c+W6lf&b!_oSS__6Gfm0Z+RyJQ?^2xKV<; n|Jo-#`iNlk@P4cCe*pgh@*j-NVVew100000NkvXXu0mjfvH$6s literal 0 HcmV?d00001 diff --git a/plugin/google_meet/resources/img/64/google_meet_na.png b/plugin/google_meet/resources/img/64/google_meet_na.png new file mode 100644 index 0000000000000000000000000000000000000000..00a76413566a1aa90563b8a1124c2e5308ff9247 GIT binary patch literal 2149 zcmV-r2%7haP)`|M%|9?rh_xTH8?*#Q`bM)TvZK6FwC}5E2GE52zKG3aUyKvyKuR zHxWWA0z}}65QLB*BzSI+Xdj3Zq!19$BnqMpM5(2|xK)uz?LrZ##DNfM?e6T(y@vyEFUY-B6A+n$`GY?)jaMd+xb+fw#D@wR+Li)Ks=mD7=F)HUQu*BI*Nh3qS$D z8$&}we>rmGi2R=cFflQ4zf$UB06qfXJpkTT`#vH9W7S74fa?JM0N{6qVO%%Rxg{z{Jf|L?UspBDk&#DJ1}0XN(uhJkcC9qQig2H|-g7-LUo zGMUNa$B)0+6#%wvj{^{lvAeU`Y)2Ij3k}zG;U~;Tgb**UDS&)F|0EH8*Dws;X{*=s zhEk~nrPP~@u}?dWb74&Y*tUHHz}Gtw>&-wYm&@=x&t;4~>^RPEItswH?MWi~zGYc3 z45Mc%xl*YFDPv`+ZFV$mjE%h`t!Ldh2E3oNJGLs_7hH+x7?%U0AnnT`T~bHf_SLUAwSh!-kf_ zrBEnfetsTTuU>`cHMaux%ViJ179qqN^?o<1qIo>7^#=w9Fg`xsw&KXLEcEyHV_;wa z=gyrAwearUyK(vQ<;ZvX%Wwn0qX52_Cu8-Dpfnkc1kXnTc!NAZQE9?2P%~ch-kHIFOPrs z)YMcq5db3kpr#lriF7bY>U_dE$LQ$j3gsI?@IAmP7K`sr>H)*8w6Zz@VRUp9!^6W3 z6QAEcs~Uze1|k|x3gG@;nfe%G7#kaFsJ!+B4MBT@ug7rGSzxeND!)B@_F(<`^@&1J zN?Gkb`_BfF0_f`%_m8W5EeP!eK7jQ}L*UM;ed~mTE-jLJKp|{esb{6Z!NE0EE}c%- z?kd%rotBb%z<-DcaUmpu)lN}LRh0}*m%+O~w!{Gbs+H=Na$0WOxDl&H2)p}JAOQS3 zDS$r(74Ig5^XJdw+O=y<6=4AY^GN}`6f7vZ34t+&)2C0@g-|aveLY@H3g9^a)GH7; z=a`w9N$d%MB>Bfg0Q~jyD`9K#FbJIlqm95h$C)!{FgrUNs~BR8>0ZKu5aRF6w8YN( zyRi-B=FOWO1F*QbxT3C~nVG3V(9byM)z5+IPaEdBrdnbEo*^O%gHUVnD_5>WuFG4I zxw$zkE-tRf>N*@|XJ=O^8x{usF7lX*${vb;`acaUEXJ?Uj;J8U>F80%R)ArMLL~s z=?d#c#Z_J#1Hg}B!EW&LgG#AVy%-Pc#I)K_36{9QL>HCtf=SUg*o#7l-?j`u2=OX_ zZw3x4bsR!%eIGW**FFn|qCd`~vBNFKfQ|yVWucuL&W3);0i+ z<0t@+5z#^;2&>lGYq+l{rG%7{02~HDZ3Ezk!VS*(h*Ihw<#IU^LfG?8dxBo{_y?zc zD1=C~aELv!>FMct03Vf7{>^_`EC_m&qR~`Zo7|$V+N2k7{RJX=Dk;!Jrt$aOKdh9x zUMv<7RW3(Cs7;JAO;?R8`R^#dQCkh`>2t(dmuyf`H|Jk;~;ib?n%&N>2dLa^S#$d&=eVqW~TTum^w@ zEMavH&@)4Ag;_7*Nh!7E&*pNu&$X(2M*sw6a&mHAsZ@GDmcKmlAb>3ZHUij|$z%+@ z=+O}BmA(E6FaS6?G&D5XuCLd0Wf#=8?bkDz%$DG)uU>`ep9%H=d{qc>w4Gz=)}db& zX6Y~p{;BYP0T>rTTyFP3dImtP%X^*&zwCY)z$b+e3mxe?cL@N6Svy$QyRJ(_G;LYd z2RfyEH$8x&rbX9vp_FEY7Wrf`jAqB zh^_;e5<)z?mILWl4p7?NJ + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/google_meet/resources/img/svg/meet_room.svg b/plugin/google_meet/resources/img/svg/meet_room.svg new file mode 100644 index 000000000..fe12d4e85 --- /dev/null +++ b/plugin/google_meet/resources/img/svg/meet_room.svg @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/google_meet/src/google_meet_plugin.class.php b/plugin/google_meet/src/google_meet_plugin.class.php new file mode 100644 index 000000000..be87472b9 --- /dev/null +++ b/plugin/google_meet/src/google_meet_plugin.class.php @@ -0,0 +1,396 @@ + + */ +class GoogleMeetPlugin extends Plugin +{ + public const TABLE_MEET_LIST = 'plugin_google_meet_room'; + public const SETTING_TITLE = 'tool_title'; + public const SETTING_ENABLED = 'google_meet_enabled'; + public const GOOGLE_MEET_URL = 'https://meet.google.com/'; + + public $isCoursePlugin = true; + + protected function __construct() + { + parent::__construct( + '1.0', + ' + Alex Aragón Calixto', + [ + self::SETTING_ENABLED => 'boolean', + self::SETTING_TITLE => 'text', + ] + ); + + $this->isAdminPlugin = true; + } + + /** + * @return string + */ + public function getTitle() + { + $title = $this->get(self::SETTING_TITLE); + + if (!empty($title)) { + return $title; + } + + return $this->get_title(); + } + + /** + * {@inheritdoc} + */ + public function get_name() + { + return 'google_meet'; + } + + /** + * Create a plugin instance. + * + * @return GoogleMeetPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * This method creates the tables required to this plugin and copies icons + * to the right places. + */ + public function install() + { + $sql = "CREATE TABLE IF NOT EXISTS ".self::TABLE_MEET_LIST." ( + id INT unsigned NOT NULL auto_increment PRIMARY KEY, + meet_name VARCHAR(250) NULL, + meet_url VARCHAR(250) NULL, + meet_description VARCHAR(250) NULL, + meet_color VARCHAR(7) NULL, + type_meet INT NOT NULL, + start_time DATETIME NULL, + end_time DATETIME NULL, + c_id INT NULL NOT NULL, + session_id INT, + activate INT + )"; + + Database::query($sql); + + // Copy icons into the main/img/icons folder + $iconName = 'google_meet'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $sourceDir = api_get_path(SYS_PLUGIN_PATH).'google_meet/resources/img/'; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $src = $sourceDir.$icon; + $dest = $destinationDir.$icon; + copy($src, $dest); + } + } + + /** + * This method drops the plugin tables and icons. + */ + public function uninstall() + { + $this->deleteCourseToolLinks(); + + $tablesToBeDeleted = [ + self::TABLE_MEET_LIST, + ]; + + foreach ($tablesToBeDeleted as $tableToBeDeleted) { + $table = Database::get_main_table($tableToBeDeleted); + $sql = "DROP TABLE IF EXISTS $table"; + Database::query($sql); + } + $dest1 = api_get_path(SYS_CODE_PATH).'img/icons/64/google_meet.png'; + $dest2 = api_get_path(SYS_CODE_PATH).'img/icons/64/google_meet_na.png'; + if (file_exists($dest1)) { + @unlink($dest1); + } + if (file_exists($dest2)) { + @unlink($dest2); + } + + $this->manageTab(false); + + // Remove icons from the main/img/icons folder + $iconName = 'google_meet'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $dest = $destinationDir.$icon; + if (is_file($dest)) { + @unlink($dest); + } + } + } + + /** + * {@inheritdoc} + * + * @return GoogleMeetPlugin + */ + public function performActionsAfterConfigure() + { + $em = Database::getManager(); + + $this->deleteCourseToolLinks(); + + if ('true' === $this->get(self::SETTING_ENABLED)) { + $courses = $em->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c')->getResult(); + + foreach ($courses as $course) { + $this->createLinkToCourseTool($this->getTitle(), $course['id']); + } + } + + return $this; + } + + public function saveMeet($values) + { + if (!is_array($values) || empty($values['meet_name'])) { + return false; + } + $table = Database::get_main_table(self::TABLE_MEET_LIST); + + $courseId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + $url = self::filterUrl($values['meet_url']); + if (!isset($values['type_meet'])) { + $values['type_meet'] = 1; + } + + $params = [ + 'meet_name' => $values['meet_name'], + 'meet_url' => $url, + 'type_meet' => $values['type_meet'], + 'meet_description' => $values['meet_description'], + 'meet_color' => $values['meet_color'], + 'c_id' => $courseId, + 'start_time' => null, + 'end_time' => null, + 'session_id' => $sessionId, + 'activate' => 1, + ]; + + $id = Database::insert($table, $params); + + if ($id > 0) { + return $id; + } + } + + public function listMeets($courseId, $sessionId = 0) + { + $list = []; + $tableMeetList = Database::get_main_table(self::TABLE_MEET_LIST); + $courseId = (int) $courseId; + $sessionId = (int) $sessionId; + + $sql = "SELECT * FROM $tableMeetList WHERE c_id = $courseId AND session_id = $sessionId AND activate = 1"; + + $result = Database::query($sql); + + if (Database::num_rows($result) > 0) { + while ($row = Database::fetch_array($result)) { + $action = Display::url( + Display::return_icon( + 'delete.png', + get_lang('Delete'), + [], + ICON_SIZE_SMALL + ), + 'meets.php?action=delete&id_room='.$row['id'], + [ + 'onclick' => 'javascript:if(!confirm('."'". + addslashes(api_htmlentities(get_lang("ConfirmYourChoice"))) + ."'".')) return false;', + ] + ); + $active = Display::return_icon('accept.png', null, [], ICON_SIZE_TINY); + if (intval($row['activate']) != 1) { + $active = Display::return_icon('error.png', null, [], ICON_SIZE_TINY); + } + + $list[] = [ + 'id' => $row['id'], + 'meet_name' => $row['meet_name'], + 'meet_url' => $row['meet_url'], + 'meet_description' => $row['meet_description'], + 'meet_color' => $row['meet_color'], + 'type_meet' => $row['type_meet'], + 'c_id' => $row['c_id'], + 'start_time' => $row['start_time'], + 'end_time' => $row['end_time'], + 'session_id' => $row['session_id'], + 'activate' => $active, + 'actions' => $action, + ]; + } + } + + return $list; + } + + public function getMeet($idMeet) + { + if (empty($idMeet)) { + return false; + } + $meet = []; + $tableMeetList = Database::get_main_table(self::TABLE_MEET_LIST); + $idMeet = (int) $idMeet; + $sql = "SELECT * FROM $tableMeetList WHERE id = $idMeet"; + + $result = Database::query($sql); + if (Database::num_rows($result) > 0) { + while ($row = Database::fetch_array($result)) { + $meet = [ + 'id' => $row['id'], + 'meet_name' => $row['meet_name'], + 'meet_url' => $row['meet_url'], + 'meet_description' => $row['meet_description'], + 'meet_color' => $row['meet_color'], + 'type_meet' => $row['type_meet'], + 'c_id' => $row['c_id'], + 'start_time' => $row['start_time'], + 'end_time' => $row['end_time'], + 'session_id' => $row['session_id'], + 'activate' => $row['activate'], + ]; + } + } + + return $meet; + } + + public function updateMeet($values) + { + if (!is_array($values) || empty($values['meet_name'])) { + return false; + } + $table = Database::get_main_table(self::TABLE_MEET_LIST); + + $courseId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + $url = self::filterUrl($values['meet_url']); + if (!isset($values['type_meet'])) { + $values['type_meet'] = 1; + } + + $params = [ + 'meet_name' => $values['meet_name'], + 'meet_url' => $url, + 'type_meet' => $values['type_meet'], + 'meet_description' => $values['meet_description'], + 'meet_color' => $values['meet_color'], + 'c_id' => $courseId, + 'start_time' => null, + 'end_time' => null, + 'session_id' => $sessionId, + 'activate' => 1, + ]; + + Database::update( + $table, + $params, + [ + 'id = ?' => [ + $values['id'], + ], + ] + ); + + return true; + } + + /** + * Delete a given meeting. + * + * @param int $idMeet Chamilo's internal ID of the meeting + * + * @return bool True on success, false on failure + */ + public function deleteMeet($idMeet) + { + if (empty($idMeet)) { + return false; + } + $idMeet = (int) $idMeet; + $tableMeetList = Database::get_main_table(self::TABLE_MEET_LIST); + $sql = "DELETE FROM $tableMeetList WHERE id = $idMeet"; + $result = Database::query($sql); + + if (Database::affected_rows($result) != 1) { + return false; + } + + return true; + } + + /** + * Delete links to the tool from the c_tool table. + */ + private function deleteCourseToolLinks() + { + Database::getManager() + ->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link') + ->execute(['category' => 'plugin', 'link' => 'google_meet/start.php%']); + } + + /** + * Do a bit of prevention on the meeting URL format. + * + * @param string $url The URL received from the user + * + * @return string Reformatted URL + */ + private function filterUrl($url) + { + if (!empty($url)) { + if (preg_match('#^'.self::GOOGLE_MEET_URL.'#', $url)) { + // The URL starts with the right Google Meet protocol and domain, do nothing + } elseif (preg_match('#^'.substr(self::GOOGLE_MEET_URL, 8).'#', $url)) { + // The URL starts with meet.google.com without the protocol. Add it + $url = 'https://'.$url; + } else { + // We assume it's just the meeting code. Add the full Google Meet prefix + if (substr($url, 0, 1) === '/') { + // Remove prefixing slash, if any + $url = substr($url, 1); + } + $url = self::GOOGLE_MEET_URL.$url; + } + } + + return $url; + } +} diff --git a/plugin/google_meet/start.php b/plugin/google_meet/start.php new file mode 100644 index 000000000..109b14437 --- /dev/null +++ b/plugin/google_meet/start.php @@ -0,0 +1,44 @@ +'; + +$plugin = GoogleMeetPlugin::create(); + +$tool_name = $plugin->get_lang('plugin_title'); +$tpl = new Template($tool_name); +$message = null; +$userId = api_get_user_id(); + +$courseInfo = api_get_course_info(); +$isTeacher = api_is_teacher(); +$isAdmin = api_is_platform_admin(); +$isStudent = api_is_student(); + +$action = isset($_GET['action']) ? $_GET['action'] : null; +$enable = $plugin->get('google_meet_enabled') == 'true'; + +$urlAddMeet = api_get_path(WEB_PLUGIN_PATH).'google_meet/meets.php?action=add&'.api_get_cidreq(); + +if ($enable) { + if ($isAdmin || $isTeacher || $isStudent) { + $meets = $plugin->listMeets($courseInfo['real_id'], api_get_session_id()); + } +} + +$tpl->assign('url_add_room', $urlAddMeet); +$tpl->assign('meets', $meets); +$tpl->assign('is_admin', $isAdmin); +$tpl->assign('is_student', $isStudent); +$tpl->assign('is_teacher', $isTeacher); +$content = $tpl->fetch('google_meet/view/home.tpl'); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/google_meet/uninstall.php b/plugin/google_meet/uninstall.php new file mode 100644 index 000000000..1d8196f31 --- /dev/null +++ b/plugin/google_meet/uninstall.php @@ -0,0 +1,4 @@ +uninstall(); diff --git a/plugin/google_meet/view/home.tpl b/plugin/google_meet/view/home.tpl new file mode 100644 index 000000000..4fe2080c6 --- /dev/null +++ b/plugin/google_meet/view/home.tpl @@ -0,0 +1,70 @@ +

    +
    +
    +
    + +
    + {% if is_admin or is_teacher %} + + + {{ 'ManageMeetAccounts'|get_plugin_lang('GoogleMeetPlugin') }} + + {% endif %} +
    + {% if meets %} +
    + {% for meet in meets %} +
    +
    +
    +
    +
    +
    +
    + +
    +
    +

    {{ meet.meet_name }}

    +
    + {{ meet.meet_description }} +
    + + + {{ 'AccessMeeting'|get_plugin_lang('GoogleMeetPlugin') }} + +
    +
    + {% if is_admin or is_teacher %} + + {% endif %} +
    + +
    +
    +
    +
    +
    +
    + {% endfor %} +
    + + {% else %} + + {% endif %} +
    +
    +
    +
    \ No newline at end of file diff --git a/plugin/google_meet/view/meets.tpl b/plugin/google_meet/view/meets.tpl new file mode 100644 index 000000000..972b5abf3 --- /dev/null +++ b/plugin/google_meet/view/meets.tpl @@ -0,0 +1,15 @@ +
    +
    +
    +
    + + {% if form_room %} + + {{ form_room }} + + {% endif %} + +
    +
    +
    +
    \ No newline at end of file diff --git a/plugin/grading_electronic/README.md b/plugin/grading_electronic/README.md new file mode 100644 index 000000000..dfeab49cb --- /dev/null +++ b/plugin/grading_electronic/README.md @@ -0,0 +1,16 @@ +# Electronic Grading Form +v0.8 + +Generates a file for a specific grades registration form. +This plugin is more to be viewed as a template for similar exports, as it will +create very specific extra fields and allow you to export very specific reports +with those extra fields. + +* Install the plugin +* Assign the `content_top` region to the plugin +* Enable the tool in the plugin configuration page + +> During the installation this plugin creates three course extra fields +> (`plugin_gradingelectronic_provider_id`, `plugin_gradingelectronic_course_id`, `plugin_gradingelectronic_coursehours`) +> and one user extra field +> (`fcdice_or_acadis_student_id`) diff --git a/plugin/grading_electronic/generate.php b/plugin/grading_electronic/generate.php new file mode 100644 index 000000000..1322ec762 --- /dev/null +++ b/plugin/grading_electronic/generate.php @@ -0,0 +1,184 @@ +get('tool_enable') === 'true'; + + if (!$toolIsEnabled) { + throw new Exception($gradingElectronic->get_lang('PluginDisabled')); + } + + $form = $gradingElectronic->getForm(); + + if (!$form->validate()) { + throw new Exception(implode('
    ', $form->_errors)); + } + + $em = Database::getManager(); + + /** @var Course $course */ + $course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id()); + /** @var Session $session */ + $session = $em->find('ChamiloCoreBundle:Session', api_get_session_id()); + + $values = $form->exportValues(); + + $cFieldValue = new ExtraFieldValue('course'); + $uFieldValue = new ExtraFieldValue('user'); + + $cFieldValue->save([ + 'variable' => GradingElectronicPlugin::EXTRAFIELD_COURSE_ID, + 'item_id' => $course->getId(), + 'value' => $values['course'], + ]); + + $item = $cFieldValue->get_item_id_from_field_variable_and_field_value( + GradingElectronicPlugin::EXTRAFIELD_COURSE_ID, + $values['course'] + ); + + $fieldProvider = $cFieldValue->get_values_by_handler_and_field_variable( + $course->getId(), + GradingElectronicPlugin::EXTRAFIELD_COURSE_PROVIDER_ID + ); + $fieldHours = $cFieldValue->get_values_by_handler_and_field_variable( + $course->getId(), + GradingElectronicPlugin::EXTRAFIELD_COURSE_HOURS + ); + + $students = []; + + if ($session) { + $criteria = Criteria::create()->where( + Criteria::expr()->eq('relationType', Session::STUDENT) + ); + + $subscriptions = $session->getUsers()->matching($criteria); + + /** @var SessionRelUser $subscription */ + foreach ($subscriptions as $subscription) { + $students[] = $subscription->getUser(); + } + } else { + $subscriptions = $course->getStudents(); + + /** @var CourseRelUser $subscription */ + foreach ($subscriptions as $subscription) { + $students[] = $subscription->getUser(); + } + } + + $cats = Category::load( + null, + null, + $course->getCode(), + null, + null, + $session ? $session->getId() : 0, + 'ORDER By id' + ); + + /** @var \Category $gradebook */ + $gradebook = $cats[0]; + /** @var \ExerciseLink $exerciseLink */ + /** commented until we get clear understanding of how to use the dates refs BT#12404. + $exerciseInfo = ExerciseLib::get_exercise_by_id($exerciseId, $course->getId()); + */ + $dateStart = new DateTime($values['range_start'].' 00:00:00', new DateTimeZone('UTC')); + $dateEnd = new DateTime($values['range_end'].' 23:59:59', new DateTimeZone('UTC')); + + $fileData = []; + $fileData[] = sprintf( + '1 %s %s%s', + $fieldProvider ? $fieldProvider['value'] : null, + $values['course'], + $dateStart->format('m/d/Y') + ); + + /** @var User $student */ + foreach ($students as $student) { + $userFinishedCourse = Category::userFinishedCourse( + $student->getId(), + $gradebook, + true + ); + if (!$userFinishedCourse) { + continue; + } + /** commented until we get clear understanding of how to use the dates refs BT#12404. + } + */ + $fieldStudent = $uFieldValue->get_values_by_handler_and_field_variable( + $student->getId(), + GradingElectronicPlugin::EXTRAFIELD_STUDENT_ID + ); + $scoretotal = $gradebook->calc_score($student->getId()); + $scoredisplay = ScoreDisplay::instance(); + $score = $scoredisplay->display_score( + $scoretotal, + SCORE_SIMPLE + ); + + /** old method to get the score. + ); + */ + $fileData[] = sprintf( + "2 %sPASS%s %s %s", + $fieldStudent ? $fieldStudent['value'] : null, + $fieldHours ? $fieldHours['value'] : null, + $score, + $dateEnd->format('m/d/Y') + ); + + if (!$gradebook->getGenerateCertificates()) { + continue; + } + + Category::generateUserCertificate( + $gradebook->get_id(), + $student->getId(), + true + ); + } + + $fileName = implode('_', [ + $gradingElectronic->get_title(), + $values['course'], + $values['range_start'], + $values['range_end'], + ]); + $fileName = api_replace_dangerous_char($fileName).'.txt'; + $fileData[] = null; + + file_put_contents( + api_get_path(SYS_ARCHIVE_PATH).$fileName, + implode("\r\n", $fileData) + ); + + echo Display::toolbarButton( + get_lang('Download'), + api_get_path(WEB_ARCHIVE_PATH).$fileName, + 'download', + 'success', + ['target' => '_blank', 'download' => $fileName] + ); +} catch (Exception $e) { + echo Display::return_message($e->getMessage(), 'error'); +} diff --git a/plugin/grading_electronic/index.php b/plugin/grading_electronic/index.php new file mode 100644 index 000000000..882d869df --- /dev/null +++ b/plugin/grading_electronic/index.php @@ -0,0 +1,16 @@ +isAllowed()) { + return; +} + +$_template['show'] = true; +$_template['plugin_title'] = $gradingElectronic->get_title(); +$_template['form'] = $gradingElectronic->getForm(); diff --git a/plugin/grading_electronic/install.php b/plugin/grading_electronic/install.php new file mode 100644 index 000000000..d9f5dcfd3 --- /dev/null +++ b/plugin/grading_electronic/install.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/grading_electronic/lang/english.php b/plugin/grading_electronic/lang/english.php new file mode 100644 index 000000000..df170755b --- /dev/null +++ b/plugin/grading_electronic/lang/english.php @@ -0,0 +1,16 @@ +Generate File button in the Assessments page. + You need assign the content_top region. +'; + +$strings['PluginDisabled'] = 'Electronic grading is disabed'; +$strings['StudentId'] = 'Student ID'; +$strings['ProviderId'] = 'Provider ID'; +$strings['CourseHours'] = 'Course hours'; diff --git a/plugin/grading_electronic/plugin.php b/plugin/grading_electronic/plugin.php new file mode 100644 index 000000000..c707eef20 --- /dev/null +++ b/plugin/grading_electronic/plugin.php @@ -0,0 +1,5 @@ +get_info(); +$plugin_info['templates'] = ['view/grading.html.twig']; diff --git a/plugin/grading_electronic/src/GradingElectronicPlugin.php b/plugin/grading_electronic/src/GradingElectronicPlugin.php new file mode 100644 index 000000000..5412d74c0 --- /dev/null +++ b/plugin/grading_electronic/src/GradingElectronicPlugin.php @@ -0,0 +1,221 @@ + 'boolean', + ] + ); + } + + /** + * @return \GradingElectronicPlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * {@inheritdoc} + */ + public function get_name() + { + return 'grading_electronic'; + } + + /** + * Actions for install. + */ + public function install() + { + $this->setUpExtraFields(); + } + + /** + * Actions for uninstall. + */ + public function uninstall() + { + $this->setDownExtraFields(); + } + + /** + * @return \FormValidator|void + */ + public function getForm() + { + $extraField = new ExtraField('course'); + $courseIdField = $extraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_ID + ); + + if (!$courseIdField) { + return null; + } + + $extraFieldValue = new ExtraFieldValue('course'); + $courseIdValue = $extraFieldValue->get_values_by_handler_and_field_variable( + api_get_course_int_id(), + self::EXTRAFIELD_COURSE_ID + ); + + $form = new FormValidator('frm_grading_electronic'); + $form->addDateRangePicker( + 'range', + get_lang('DateRange'), + true, + [ + 'id' => 'range', + 'format' => 'YYYY-MM-DD', + 'timePicker' => 'false', + 'validate_format' => 'Y-m-d', + ] + ); + $form->addText('course', $this->get_lang('CourseId')); + $form->addButtonDownload(get_lang('Generate')); + $form->addRule('course', get_lang('ThisFieldIsRequired'), 'required'); + $form->setDefaults([ + 'course' => $courseIdValue ? $courseIdValue['value'] : null, + ]); + + return $form; + } + + /** + * Check if the current use is allowed to see the button. + * + * @return bool + */ + public function isAllowed() + { + $allowed = api_is_teacher() || api_is_platform_admin() || api_is_course_tutor(); + + if (!$allowed) { + return false; + } + + $toolIsEnabled = $this->get('tool_enable') === 'true'; + + if (!$toolIsEnabled) { + return false; + } + + return true; + } + + /** + * Create extra fields for this plugin. + */ + private function setUpExtraFields() + { + $uExtraField = new ExtraField('user'); + + if (!$uExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_STUDENT_ID + )) { + $uExtraField->save([ + 'variable' => self::EXTRAFIELD_STUDENT_ID, + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'display_text' => $this->get_lang('StudentId'), + 'visible_to_self' => true, + 'changeable' => true, + ]); + } + + $cExtraField = new ExtraField('course'); + + if (!$cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_PROVIDER_ID + )) { + $cExtraField->save([ + 'variable' => self::EXTRAFIELD_COURSE_PROVIDER_ID, + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'display_text' => $this->get_lang('ProviderId'), + 'visible_to_self' => true, + 'changeable' => true, + ]); + } + + if (!$cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_ID + )) { + $cExtraField->save([ + 'variable' => self::EXTRAFIELD_COURSE_ID, + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'display_text' => $this->get_lang('CourseId'), + 'visible_to_self' => true, + 'changeable' => true, + ]); + } + + if (!$cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_HOURS + )) { + $cExtraField->save([ + 'variable' => self::EXTRAFIELD_COURSE_HOURS, + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + 'display_text' => $this->get_lang('CourseHours'), + 'visible_to_self' => true, + 'changeable' => true, + ]); + } + } + + /** + * Remove extra fields for this plugin. + */ + private function setDownExtraFields() + { + $uExtraField = new ExtraField('user'); + $studentIdField = $uExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_STUDENT_ID + ); + + if ($studentIdField) { + $uExtraField->delete($studentIdField['id']); + } + + $cExtraField = new ExtraField('course'); + $providerIdField = $cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_PROVIDER_ID + ); + $courseIdField = $cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_ID + ); + $courseHoursField = $cExtraField->get_handler_field_info_by_field_variable( + self::EXTRAFIELD_COURSE_HOURS + ); + + if ($providerIdField) { + $cExtraField->delete($providerIdField['id']); + } + + if ($courseIdField) { + $cExtraField->delete($courseIdField['id']); + } + + if ($courseHoursField) { + $cExtraField->delete($courseHoursField['id']); + } + } +} diff --git a/plugin/grading_electronic/uninstall.php b/plugin/grading_electronic/uninstall.php new file mode 100644 index 000000000..5f8498a7a --- /dev/null +++ b/plugin/grading_electronic/uninstall.php @@ -0,0 +1,4 @@ +uninstall(); diff --git a/plugin/grading_electronic/view/grading.html.twig b/plugin/grading_electronic/view/grading.html.twig new file mode 100644 index 000000000..c2cb99134 --- /dev/null +++ b/plugin/grading_electronic/view/grading.html.twig @@ -0,0 +1,40 @@ +{% if grading_electronic.show %} + + + + +{% endif %} diff --git a/plugin/h5p/README.md b/plugin/h5p/README.md new file mode 100644 index 000000000..5cbe13248 --- /dev/null +++ b/plugin/h5p/README.md @@ -0,0 +1,36 @@ +H5P plugin +=== + +This plugin is only compatible with Chamilo version 1.11.10 and above. + +This plugin allows you to create H5P resources directly from inside Chamilo, +using the Open Source H5P library. + +To enable: + +* install the plugin +* mark it as "enabled" (inside the plugin configuration page) +* set the plugin to the region "pre_footer" +* change permissions on disk for the plugin/h5p/cache-h5p/launch folder to be writeable by the web server + +Once enabled, permissions granted for the web server to write in plugin/h5p/cache-h5p/launch/ +and the pre_footer region set for the plugin, a new H5P logo will appear in the +new document creation form (the WYSIWIG editor), as a small icon at the end of +the editor interface, exclusively in the learning path document creation +interface (don't look for it anywhere else at this point). + +However, it is still very limited at this stage: the H5P files are created in a +common directory for all teachers. This means all teachers can re-use contents of +others (which is good) but also that all teachers can remove contents of others +(which is bad). + +We hope to be improving this in the future, but we will not be able, in future +versions, to assign content created in this version to the correct teacher. + +We suggest you consider this plugin as a Beta version. + +To enable tools in the document editor, please enable it in region pre_footer. + +Other docs: + +https://www.ludiscape.com/ressources/resources-elearning-en/integration-of-h5p-into-our-lms-chamilo/ \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/.eslintrc.json b/plugin/h5p/cache-h5p/.eslintrc.json new file mode 100644 index 000000000..bdd5594ba --- /dev/null +++ b/plugin/h5p/cache-h5p/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "rules": { + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "warn", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "constructor-super": "warn", + "valid-typeof": "warn" + }, + "globals": { + "H5PIntegration": true + } +} \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/.github/workflows/cypress.yml b/plugin/h5p/cache-h5p/.github/workflows/cypress.yml new file mode 100644 index 000000000..953e9727f --- /dev/null +++ b/plugin/h5p/cache-h5p/.github/workflows/cypress.yml @@ -0,0 +1,16 @@ +name: Node CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [8.x, 10.x, 12.x] + + steps: + - name: Cypress.io + uses: cypress-io/github-action@v1.16.1 diff --git a/plugin/h5p/cache-h5p/.gitignore b/plugin/h5p/cache-h5p/.gitignore new file mode 100644 index 000000000..e7c660a94 --- /dev/null +++ b/plugin/h5p/cache-h5p/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +cypress/videos \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/CHANGELOG.md b/plugin/h5p/cache-h5p/CHANGELOG.md new file mode 100644 index 000000000..24e812850 --- /dev/null +++ b/plugin/h5p/cache-h5p/CHANGELOG.md @@ -0,0 +1,17 @@ +# 2.1.0 +* Add optional libraryPath option for serving libraries from another source, thanks @tdxdave + +# 2.0.2 +* Check if main library has dependencies before loading them + +# 2.0.1 +* fix paths for h5p assets + +# 2.0.0 +* Switched to Webpack +* Using module system, can be imported via ES6, CommonJS, AMD or Globals +* Hide H5P frame by default +* Move away from being a jQuery plugin +* Include H5P JS files directly in this project +* Remove bower +* Add tests \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/LICENSE b/plugin/h5p/cache-h5p/LICENSE new file mode 100644 index 000000000..9621b9de7 --- /dev/null +++ b/plugin/h5p/cache-h5p/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Tunapanda + +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/plugin/h5p/cache-h5p/README.md b/plugin/h5p/cache-h5p/README.md new file mode 100644 index 000000000..66909be89 --- /dev/null +++ b/plugin/h5p/cache-h5p/README.md @@ -0,0 +1,85 @@ +# h5p-standalone 2.1.0 +Display H5P content without using a webserver + + +## Install +``` +yarn add h5p-standalone +``` + +## Basic Usage + +```javascript +import { H5P } from 'h5p-standalone'; // ES6 +// const { H5P } = require('h5p-standalone'); AMD +// + + + + +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/drag-the-words.html b/plugin/h5p/cache-h5p/launch/drag-the-words.html new file mode 100644 index 000000000..b68de0076 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/drag-the-words.html @@ -0,0 +1,23 @@ + + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/guesstheanswer.html b/plugin/h5p/cache-h5p/launch/guesstheanswer.html new file mode 100644 index 000000000..5a1674efe --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/guesstheanswer.html @@ -0,0 +1,23 @@ + + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/memory.html b/plugin/h5p/cache-h5p/launch/memory.html new file mode 100644 index 000000000..0d740cbdc --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/memory.html @@ -0,0 +1,23 @@ + + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/mots-caches.html b/plugin/h5p/cache-h5p/launch/mots-caches.html new file mode 100644 index 000000000..1e3cdbc8a --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/mots-caches.html @@ -0,0 +1,23 @@ + + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/multiple.html b/plugin/h5p/cache-h5p/launch/multiple.html new file mode 100644 index 000000000..32db890b2 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/multiple.html @@ -0,0 +1,34 @@ + + + + + + + + + +
    +
    + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/css/drop-theme-arrows-bounce.min.css b/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/css/drop-theme-arrows-bounce.min.css new file mode 100644 index 000000000..c5c80302d --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/css/drop-theme-arrows-bounce.min.css @@ -0,0 +1 @@ +.drop-element,.drop-element *,.drop-element :after,.drop-element :before,.drop-element:after,.drop-element:before{box-sizing:border-box}.drop-element{position:absolute;display:none}.drop-element.drop-open{display:block}.drop-element.drop-theme-arrows-bounce .drop-content{border-radius:5px;position:relative;font-family:inherit;background:#fff;color:#444;padding:1em;font-size:1.1em;line-height:1.5em;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-filter:drop-shadow(0 1px 4px rgba(0, 0, 0, .2));filter:drop-shadow(0 1px 4px rgba(0, 0, 0, .2))}.drop-element.drop-theme-arrows-bounce .drop-content:before{content:"";display:block;position:absolute;width:0;height:0;border-color:transparent;border-width:12px;border-style:solid}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-center .drop-content:before{top:100%;left:50%;margin-left:-12px;border-top-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-center .drop-content{margin-top:12px}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-center .drop-content:before{bottom:100%;left:50%;margin-left:-12px;border-bottom-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-right.drop-element-attached-middle .drop-content:before{left:100%;top:50%;margin-top:-12px;border-left-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-left.drop-element-attached-middle .drop-content:before{right:100%;top:50%;margin-top:-12px;border-right-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-left.drop-target-attached-bottom .drop-content:before{bottom:100%;left:12px;border-bottom-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-right.drop-target-attached-bottom .drop-content:before{bottom:100%;right:12px;border-bottom-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-top .drop-content:before{top:100%;left:12px;border-top-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-top .drop-content:before{top:100%;right:12px;border-top-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-right.drop-target-attached-left .drop-content:before{top:12px;left:100%;border-left-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-left.drop-target-attached-right .drop-content:before{top:12px;right:100%;border-right-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-left .drop-content:before{bottom:12px;left:100%;border-left-color:#fff}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-right .drop-content:before{bottom:12px;right:100%;border-right-color:#fff}.drop-element.drop-theme-arrows-bounce{max-width:100%;max-height:100%;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-transition:opacity .1s;transition:opacity .1s;opacity:0}.drop-element.drop-theme-arrows-bounce .drop-content{-webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.265,1.55);transition:transform .3s cubic-bezier(0,0,.265,1.55);-webkit-transform:scale(0) translateZ(0);transform:scale(0) translateZ(0)}.drop-element.drop-theme-arrows-bounce.drop-open{display:none}.drop-element.drop-theme-arrows-bounce.drop-open-transitionend{display:block}.drop-element.drop-theme-arrows-bounce.drop-after-open{-webkit-transition:none;transition:none;opacity:1}.drop-element.drop-theme-arrows-bounce.drop-after-open .drop-content{-webkit-transform:scale(1) translateZ(0);transform:scale(1) translateZ(0)}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-center .drop-content{margin-bottom:12px;-webkit-transform-origin:50%,calc(100% + 12px);-ms-transform-origin:50%,calc(100% + 12px);transform-origin:50%,calc(100% + 12px)}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-center .drop-content{-webkit-transform-origin:50%,-12px;-ms-transform-origin:50%,-12px;transform-origin:50%,-12px}.drop-element.drop-theme-arrows-bounce.drop-element-attached-right.drop-element-attached-middle .drop-content{margin-right:12px;-webkit-transform-origin:calc(100% + 12px),50%;-ms-transform-origin:calc(100% + 12px),50%;transform-origin:calc(100% + 12px),50%}.drop-element.drop-theme-arrows-bounce.drop-element-attached-left.drop-element-attached-middle .drop-content{margin-left:12px;-webkit-transform-origin:-12px,50%;-ms-transform-origin:-12px,50%;transform-origin:-12px,50%}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-left.drop-target-attached-bottom .drop-content{margin-top:12px;-webkit-transform-origin:0,-12px;-ms-transform-origin:0,-12px;transform-origin:0,-12px}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-right.drop-target-attached-bottom .drop-content{margin-top:12px;-webkit-transform-origin:100%,-12px;-ms-transform-origin:100%,-12px;transform-origin:100%,-12px}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-top .drop-content{margin-bottom:12px;-webkit-transform-origin:0,calc(100% + 12px);-ms-transform-origin:0,calc(100% + 12px);transform-origin:0,calc(100% + 12px)}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-top .drop-content{margin-bottom:12px;-webkit-transform-origin:100%,calc(100% + 12px);-ms-transform-origin:100%,calc(100% + 12px);transform-origin:100%,calc(100% + 12px)}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-right.drop-target-attached-left .drop-content{margin-right:12px;-webkit-transform-origin:calc(100% + 12px),0;-ms-transform-origin:calc(100% + 12px),0;transform-origin:calc(100% + 12px),0}.drop-element.drop-theme-arrows-bounce.drop-element-attached-top.drop-element-attached-left.drop-target-attached-right .drop-content{margin-left:12px;-webkit-transform-origin:-12px,0;-ms-transform-origin:-12px,0;transform-origin:-12px,0}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-right.drop-target-attached-left .drop-content{margin-right:12px;-webkit-transform-origin:calc(100% + 12px),100%;-ms-transform-origin:calc(100% + 12px),100%;transform-origin:calc(100% + 12px),100%}.drop-element.drop-theme-arrows-bounce.drop-element-attached-bottom.drop-element-attached-left.drop-target-attached-right .drop-content{margin-left:12px;-webkit-transform-origin:-12px,100%;-ms-transform-origin:-12px,100%;transform-origin:-12px,100%} \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/js/drop.min.js b/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/js/drop.min.js new file mode 100644 index 000000000..262412d48 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/Drop-1.0/js/drop.min.js @@ -0,0 +1,7 @@ +var oldDrop = window.Drop; +var oldTether = window.Tether; +Tether = H5P.Tether; +!function(t,e){"function"==typeof define&&define.amd?define(["tether"],e):"object"==typeof exports?module.exports=e(require("tether")):t.Drop=e(t.Tether)}(this,function(t){"use strict";function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function o(t){var e=t.split(" "),n=a(e,2),o=n[0],i=n[1];if(["left","right"].indexOf(o)>=0){var s=[i,o];o=s[0],i=s[1]}return[o,i].join(" ")}function i(t,e){for(var n=void 0,o=[];-1!==(n=t.indexOf(e));)o.push(t.splice(n,1));return o}function s(){var a=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],u=function(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];return new(r.apply(b,[null].concat(e)))};p(u,{createContext:s,drops:[],defaults:{}});var g={classPrefix:"drop",defaults:{position:"bottom left",openOn:"click",beforeClose:null,constrainToScrollParent:!0,constrainToWindow:!0,classes:"",remove:!1,tetherOptions:{}}};p(u,g,a),p(u.defaults,g.defaults,a.defaults),"undefined"==typeof x[u.classPrefix]&&(x[u.classPrefix]=[]),u.updateBodyClasses=function(){for(var t=!1,e=x[u.classPrefix],n=e.length,o=0;n>o;++o)if(e[o].isOpened()){t=!0;break}t?d(document.body,u.classPrefix+"-open"):c(document.body,u.classPrefix+"-open")};var b=function(s){function r(t){if(e(this,r),l(Object.getPrototypeOf(r.prototype),"constructor",this).call(this),this.options=p({},u.defaults,t),this.target=this.options.target,"undefined"==typeof this.target)throw new Error("Drop Error: You must provide a target.");var n="data-"+u.classPrefix,o=this.target.getAttribute(n);o&&(this.options.content=o);for(var i=["position","openOn"],s=0;s=0)for(var n=function(e){t.toggle(e),e.preventDefault()},o=function(e){t.isOpened()&&(e.target===t.drop||t.drop.contains(e.target)||e.target===t.target||t.target.contains(e.target)||t.close(e))},i=0;i=0&&(this._on(this.target,"mouseover",h),this._on(this.drop,"mouseover",h),this._on(this.target,"mouseout",l),this._on(this.drop,"mouseout",l)),e.indexOf("focus")>=0&&(this._on(this.target,"focus",h),this._on(this.drop,"focus",h),this._on(this.target,"blur",l),this._on(this.drop,"blur",l))}}},{key:"isOpened",value:function(){return this.drop?f(this.drop,u.classPrefix+"-open"):void 0}},{key:"toggle",value:function(t){this.isOpened()?this.close(t):this.open(t)}},{key:"open",value:function(t){var e=this;this.isOpened()||(this.drop.parentNode||document.body.appendChild(this.drop),"undefined"!=typeof this.tether&&this.tether.enable(),d(this.drop,u.classPrefix+"-open"),d(this.drop,u.classPrefix+"-open-transitionend"),setTimeout(function(){e.drop&&d(e.drop,u.classPrefix+"-after-open")}),"undefined"!=typeof this.tether&&this.tether.position(),this.trigger("open"),u.updateBodyClasses())}},{key:"_transitionEndHandler",value:function(t){t.target===t.currentTarget&&(f(this.drop,u.classPrefix+"-open")||c(this.drop,u.classPrefix+"-open-transitionend"),this.drop.removeEventListener(m,this.transitionEndHandler))}},{key:"beforeCloseHandler",value:function(t){var e=!0;return this.isClosing||"function"!=typeof this.options.beforeClose||(this.isClosing=!0,e=this.options.beforeClose(t,this)!==!1),this.isClosing=!1,e}},{key:"close",value:function(t){this.isOpened()&&this.beforeCloseHandler(t)&&(c(this.drop,u.classPrefix+"-open"),c(this.drop,u.classPrefix+"-after-open"),this.drop.addEventListener(m,this.transitionEndHandler),this.trigger("close"),"undefined"!=typeof this.tether&&this.tether.disable(),u.updateBodyClasses(),this.options.remove&&this.remove(t))}},{key:"remove",value:function(t){this.close(t),this.drop.parentNode&&this.drop.parentNode.removeChild(this.drop)}},{key:"position",value:function(){this.isOpened()&&"undefined"!=typeof this.tether&&this.tether.position()}},{key:"destroy",value:function(){this.remove(),"undefined"!=typeof this.tether&&this.tether.destroy();for(var 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%0D}#zSrRb8FuaocFSDv)#ldj3 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 literal 0 HcmV?d00001 diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.svg b/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.svg new file mode 100644 index 000000000..d05688e9e --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.svg @@ -0,0 +1,655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.ttf b/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..26dea7951a73079223b50653c455c5adf46a4648 GIT binary patch literal 142072 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 +|+&Vdls@0SAya!8#E9iVniRwHu0GY;H*n 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�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%0D0!#Bo^>xe$Z4SmykflG_VnkLvLv4@e#4_y4Q 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�>=olG4!T4BYNCc4f{&*Kgc`m= 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: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{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{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{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: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-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-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"} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/library.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/library.json new file mode 100644 index 000000000..f6943f441 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/FontAwesome-4.5/library.json @@ -0,0 +1,16 @@ +{ + "title": "Font Awesome", + "contentType": "Font", + "majorVersion": 4, + "minorVersion": 5, + "patchVersion": 4, + "runnable": 0, + "machineName": "FontAwesome", + "license": "MIT", + "author": "Dave Gandy", + "preloadedCss": [ + { + "path": "h5p-font-awesome.min.css" + } + ] +} \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/af.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/af.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/af.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ar.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ar.json new file mode 100644 index 000000000..451ae4859 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ar.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "الملفات المبدئية" + }, + { + "label": "وضع مشغل الصوت", + "description": "اختيار نوع مشغل الصوت", + "options": [ + { + "label": "مصغر" + }, + { + "label": "مكبر" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "ملء الحاوية" + }, + { + "label": "تفعيل ادوات التحكم", + "description": "ادوات التحكم تسمح للمستخدم على سبيل المثال وقف الصوت" + }, + { + "label": "تفعيل التشغيل التلقائي", + "description": "مع التشغيل التلقائي يبدأ الصوت للعب على الفور. إذا تم تعطيل التشغيل التلقائي يقوم المستخدم بالضغط على زر التشغيل لبدء الصوت" + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "اسم ملف الصوت", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bg.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bg.json new file mode 100644 index 000000000..63bda4001 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bg.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Изходни файлове" + }, + { + "label": "Режим на плейъра", + "description": "Изберете оформлението на плейъра.", + "options": [ + { + "label": "Минималистичен" + }, + { + "label": "Пълен" + }, + { + "label": "Прозрачен" + } + ] + }, + { + "label": "Напаснат към рамката" + }, + { + "label": "Активиране на контролите", + "description": "Контролите позволяват на потребителя например да постави на пауза на звука" + }, + { + "label": "Активиране на автоматичното възпроизвеждане", + "description": "С автоматичното пускане аудиото започва да се възпроизвежда веднага. Ако функцията за автоматично пускане е забранена, потребителят натиска бутон за възпроизвеждане, за да стартира аудиото." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Заглавие на аудио съдържанието", + "default": "Аудио" + }, + { + "label": "Аудиото не поддържа съобщение", + "default": "Вашият браузър не поддържа това аудио" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bs.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bs.json new file mode 100644 index 000000000..956fe14a0 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/bs.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Izvorni fajlovi" + }, + { + "label": "Izgled playera", + "description": "Odaberi izgled playera.", + "options": [ + { + "label": "Minimalno" + }, + { + "label": "Potpuno" + }, + { + "label": "Transparentno" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Omogući kontrolu", + "description": "Kontrole omogućavaju korisniku da pauzira audio." + }, + { + "label": "Omogući autoplay", + "description": "Sa autoplay audio počinje automatski. Ako je autoplay onemogućen korisnik pritiskom na play pusta audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Pusti audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pauziraj audio" + }, + { + "label": "Naziv audio sadržaja", + "default": "Audio" + }, + { + "label": "Poruka za audio koji nije podržan.", + "default": "Vaš pretraživač ne podržava ovaj audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ca.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ca.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ca.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/cs.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/cs.json new file mode 100644 index 000000000..68a035460 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/cs.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Zdrojové soubory" + }, + { + "label": "Režim přehrávače", + "description": "Vyberte rozložení přehrávače.", + "options": [ + { + "label": "Minimalistický" + }, + { + "label": "Úplný" + }, + { + "label": "Transparentní" + } + ] + }, + { + "label": "Přizpůsobit obalu" + }, + { + "label": "Povolit ovládací prvky", + "description": "Ovládací prvky umožňují uživateli například pozastavit zvuk" + }, + { + "label": "Povolit automatické přehrávání", + "description": "Při automatickém přehrávání se zvuk začne okamžitě přehrávat. Pokud je automatické přehrávání zakázáno, uživatel spustí zvuk stiskem tlačítka přehrávání." + }, + { + "label": "Přehrát zvuk (Čtecí zařízení)", + "default": "Přehrát zvuk" + }, + { + "label": "Pozastavit zvuk (Čtecí zařízení)", + "default": "Pozastavit zvuk" + }, + { + "label": "Název zvukového obsahu", + "default": "Audio" + }, + { + "label": "Zvuk není podporován", + "default": "Váš prohlížeč nepodporuje tento zvuk" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/da.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/da.json new file mode 100644 index 000000000..b65aed6fb --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/da.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Vælg fil" + }, + { + "label": "Indstillinger for afspilning", + "description": "Vælg layout for afspiller.", + "options": [ + { + "label": "Minimalistisk" + }, + { + "label": "Fuld" + }, + { + "label": "Gennemsigtig" + } + ] + }, + { + "label": "Tilpas til område" + }, + { + "label": "Aktiver kontrolpanel", + "description": "Kontrolpanel giver mulighed for at pause afspilningen." + }, + { + "label": "Aktiver automatisk afspilning", + "description": "Vælges automatisk afspilning startes afspilningen med det samme. Hvis antomatisk afspilning er fravalgt skal brugeren starte afspilning ved at klikke på playknappen." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio fil-navn", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/de.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/de.json new file mode 100644 index 000000000..83b4c2fd7 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/de.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Audioquellen" + }, + { + "label": "Audioplayermodus", + "description": "Wähle den gewünschten Audioplayer-Modus.", + "options": [ + { + "label": "Minimaler Funktionsumfang" + }, + { + "label": "Voller Funktionsumfang" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Verfügbaren Platz nutzen" + }, + { + "label": "Steuerelemente aktivieren", + "description": "Dies gibt dem Nutzer z. B. die Möglichkeit, das Abspielen zu pausieren." + }, + { + "label": "Automatische Wiedergabe aktivieren", + "description": "Bei aktivierter, automatischer Wiedergabe wird die Tondatei sofort abgespielt. Ansonsten wird die Tondatei erst abgespielt, wenn der Nutzer den Play-Button gedrückt hat." + }, + { + "label": "Audio abspielen (Screenreader)", + "default": "Audio abspielen" + }, + { + "label": "Audio pausieren (Screenreader)", + "default": "Audio pausieren" + }, + { + "label": "Name des Audioinhalts", + "default": "Audio" + }, + { + "label": "Hinweis, dass keine Tonaufnahmen abgespielt werden können", + "default": "Dein Browser unterstützt diese Tondatei nicht." + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/el.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/el.json new file mode 100644 index 000000000..bf6f37234 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/el.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Αρχεία προέλευσης" + }, + { + "label": "Μορφή προγράμματος αναπαραγωγής", + "description": "Επιλέξτε την εμφάνιση του προγράμματος αναπαραγωγής.", + "options": [ + { + "label": "Απλή" + }, + { + "label": "Πλήρης" + }, + { + "label": "Διάφανη" + } + ] + }, + { + "label": "Προσαρμογή στην περιοχή ενσωμάτωσης" + }, + { + "label": "Ενεργοποίηση κουμπιών ελέγχου προγράμματος αναπαραγωγής", + "description": "Τα κουμπιά ελέγχου του προγράμματος αναπαραγωγής επιτρέπουν στον χρήστη π.χ. να κάνει παύση (pause) του ήχου" + }, + { + "label": "Ενεργοποίηση αυτόματης αναπαραγωγής", + "description": "Με την αυτόματη αναπαραγωγή ο ήχος ξεκινά να παίζει αυτόματα. Εάν η αυτόματη αναπαραγωγή είναι απενεργοποιημένη, ο χρήστης χρειάζεται να πατήσει το κουμπί αναπαραγωγής (play) του ήχου για να ξεκινήσει ο ήχος να παίζει." + }, + { + "label": "Αναπαραγωγή ήχου (Ακουστική υποβοήθηση)", + "default": "Αναπαραγωγή ήχου" + }, + { + "label": "Παύση ήχου (Ακουστική υποβοήθηση)", + "default": "Παύση ήχου" + }, + { + "label": "Όνομα περιεχομένου ήχου", + "default": "Ήχος" + }, + { + "label": "Μήνυμα μη υποστηριζόμενου αρχείου ήχου", + "default": "Ο φυλλομετρητής δεν υποστηρίζει το συγκεκριμένο αρχείο ήχου" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es-mx.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es-mx.json new file mode 100644 index 000000000..6b4b2f531 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es-mx.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Archivo de origen" + }, + { + "label": "Modo reproductor", + "description": "Seleccione el diseño del reproductor.", + "options": [ + { + "label": "Minimalista" + }, + { + "label": "Completo" + }, + { + "label": "Transparente" + } + ] + }, + { + "label": "Ajustar al marco" + }, + { + "label": "Habilitar controles", + "description": "Los controles permiten al usuario por ejemplo pausar el audio" + }, + { + "label": "Habilitar reproduccion automatica", + "description": "Con la reproducción automática el audio empieza a reproducirse inmediatamente. Si la reproducción automática está deshabilitada, el usuario presiona un botón de reproducción para iniciar el audio." + }, + { + "label": "Reproducir audio (Readspeaker)", + "default": "Reproducir audio" + }, + { + "label": "Pausar audio (Readspeaker)", + "default": "Pausar audio" + }, + { + "label": "Nombre contenido de audio", + "default": "Audio" + }, + { + "label": "Mensaje de audio no permitido", + "default": "Su navegador no es compatible con este audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es.json new file mode 100644 index 000000000..b3cc84db4 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/es.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Archivo de origen" + }, + { + "label": "Modo reproductor", + "description": "Seleccione el diseño del reproductor.", + "options": [ + { + "label": "Minimalista" + }, + { + "label": "Completo" + }, + { + "label": "Transparente" + } + ] + }, + { + "label": "Ajustar al marco" + }, + { + "label": "Habilitar controles", + "description": "Los controles permiten al usuario por ejemplo pausar el audio" + }, + { + "label": "Habilitar reproduccion automatica", + "description": "Con la reproducción automática el audio empieza a reproducirse inmediatamente. Si la reproducción automática está deshabilitada, el usuario presiona un botón de reproducción para iniciar el audio." + }, + { + "label": "Reproducir audio (Herramienta de lectura)", + "default": "Reproducir audio" + }, + { + "label": "Pausar audio (Herramienta de lectura)", + "default": "Pausar audio" + }, + { + "label": "Nombre contenido de audio", + "default": "Audio" + }, + { + "label": "Mensaje de audio no permitido", + "default": "Su navegador no es compatible con este audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/et.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/et.json new file mode 100644 index 000000000..46c51a3ee --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/et.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Lähtefailid" + }, + { + "label": "Pleieri teema", + "description": "Vali pleieri teema.", + "options": [ + { + "label": "Minimalistlik" + }, + { + "label": "Täielik" + }, + { + "label": "Läbipaistev" + } + ] + }, + { + "label": "Täida ümbrisraam" + }, + { + "label": "Luba juhised", + "description": "Juhised võimaldavad kasutajal näiteks heli mängimist peatada." + }, + { + "label": "Luba automaatne esitus", + "description": "Automaatse esituse korral hakkab heli koheselt mängima. Kui automaatne esitus on keelatud, tuleb kasutajal ise nupuvajutusega heli mängimine käivitada." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Helisisu nimi", + "default": "Heli" + }, + { + "label": "Heli ei ole toetatud sõnum", + "default": "Su brauser ei toeta heli esitamist" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/eu.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/eu.json new file mode 100644 index 000000000..09b308d48 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/eu.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Iturri-fitxategiak" + }, + { + "label": "Erreprodukzio modua", + "description": "Hautatu erreproduktorearen itxura.", + "options": [ + { + "label": "Minimalista" + }, + { + "label": "Osoa" + }, + { + "label": "Gardena" + } + ] + }, + { + "label": "Markora egokitu" + }, + { + "label": "Gaitu kontrolak", + "description": "Kontrolek aukera ematen diote erabiltzaileari, adibidez, audioa gelditzeko" + }, + { + "label": "Gaitu erreprodukzio automatikoa", + "description": "Erreprodukzio automatikoarekin audioa instantean hasiko da. Erreprodukzio automatikoa ez ba dago aktibatua erabiltzaileak erreproduzitzeko botoia sakatu behar du audio hasteko." + }, + { + "label": "Erreproduzitu audioa (irakugailuetarako)", + "default": "Erreproduzitu audioa" + }, + { + "label": "Pausatu audioa (irakugailuetarako)", + "default": "Pausatu audioa" + }, + { + "label": "Audio edukiaren izena", + "default": "Audioa" + }, + { + "label": "Audioa ez da onartzen mezua", + "default": "Zure nabigatzaileak ez du onartzen audio hau." + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fi.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fi.json new file mode 100644 index 000000000..921cf69ed --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fi.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Lähdetiedostot" + }, + { + "label": "Soittimen teema", + "description": "Valitse soittimelle ulkoasu.", + "options": [ + { + "label": "Minimalistinen" + }, + { + "label": "Täysi" + }, + { + "label": "Läpinäkyvä" + } + ] + }, + { + "label": "Sovita kehykseen" + }, + { + "label": "Salli säätimet", + "description": "Säätimillä käyttäjä voi esimerkiksi pysäyttää äänen toiston." + }, + { + "label": "Salli automaattinen toisto", + "description": "Toisto alkaa välittömästi. Muussa tapauksessa käyttäjä aloittaa toiston." + }, + { + "label": "Toista audio (Readspeakerilla)", + "default": "Toista audio" + }, + { + "label": "Keskeytä audio (Readspeakerilla)", + "default": "Keskeytä audio" + }, + { + "label": "Äänisisällön nimi", + "default": "Ääni" + }, + { + "label": "Ääntä ei tueta -viesti", + "default": "Selaimesi ei tue tätä ääntä." + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fr.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fr.json new file mode 100644 index 000000000..0c0be2357 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/fr.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Fichiers sources" + }, + { + "label": "Mode d'affichage du lecteur", + "description": "Selectionnez le mode d'affichage du lecteur.", + "options": [ + { + "label": "Minimaliste" + }, + { + "label": "Complet" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Remplir le contenant" + }, + { + "label": "Activer les contrôles", + "description": "Les contrôles permettent, par exemple, à l'utilisateur de mettre le son sur pause" + }, + { + "label": "Activer le démarrage automatique", + "description": "Le son démarre directement sans appuyer sur un bouton. Si cette option est désactivée, l'utilisateur doit appuyer sur le bouton PLAY pour lancer la lecture." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Nom du contenu audio", + "default": "Audio" + }, + { + "label": "Message L'audio n'est pas supporté", + "default": "Votre navigateur ne supporte pas l'audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/he.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/he.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/he.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/hu.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/hu.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/hu.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/it.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/it.json new file mode 100644 index 000000000..f4351d4a0 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/it.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "File sorgenti" + }, + { + "label": "Modalità player", + "description": "Seleziona il layout del player.", + "options": [ + { + "label": "Minimalista" + }, + { + "label": "Intero" + }, + { + "label": "Trasparente" + } + ] + }, + { + "label": "Adatta al contenitore" + }, + { + "label": "Attiva controlli", + "description": "I controlli consentono all'utente di mettere, per esempio, in pausa l'audio" + }, + { + "label": "Attiva autoplay", + "description": "Con l'autoplay l'audio si avvia immediatamente. Se l'autoplay è disattivato l'utente premerà il pulsante play per avviarlo." + }, + { + "label": "Avvia l'audio (lettore vocale)", + "default": "Avvia l'audio" + }, + { + "label": "Metti in pausa l'audio (lettore vocale)", + "default": "Metti in pausa l'audio" + }, + { + "label": "Nome del contenuto dell'audio", + "default": "Audio" + }, + { + "label": "Messaggio per audio non supportato", + "default": "Il tuo browser non supporta questo file audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ja.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ja.json new file mode 100644 index 000000000..5b25c0104 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ja.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "ソースファイル" + }, + { + "label": "プレーヤーモード", + "description": "プレーヤーのレイアウトを選択してください。", + "options": [ + { + "label": "最小限" + }, + { + "label": "最大" + }, + { + "label": "透明" + } + ] + }, + { + "label": "ラッパーに合わせる" + }, + { + "label": "コントロールを有効にします", + "description": "コントロールを使用すると、たとえば、オーディオを一時停止することができます" + }, + { + "label": "自動再生を有効にします", + "description": "自動再生では、オーディオはすぐに再生を開始します。 自動再生が無効になっている場合、ユーザーは再生ボタンを押して音声を開始します。" + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "オーディオコンテンツ名", + "default": "オーディオ" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ko.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ko.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ko.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nb.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nb.json new file mode 100644 index 000000000..1088e26ad --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nb.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Lydfiler" + }, + { + "label": "Avspiller modus", + "description": "Velg avspiller modus.", + "options": [ + { + "label": "Minimal" + }, + { + "label": "Full" + }, + { + "label": "Gjennomsiktig" + } + ] + }, + { + "label": "Tilpass størrelse" + }, + { + "label": "Slå på kontroller", + "description": "Kontrollene lar bl.a. brukeren sette lydavspillingen på pause" + }, + { + "label": "Slå på automatisk avspilling", + "description": "Med automatisk avspilling begynner lyden å spille av seg selv med en gang. Hvis automatisk avspilling er avslått må brukeren trykke for å starte avspillingen." + }, + { + "label": "Spill lyd (Readspeaker)", + "default": "Spill lyd" + }, + { + "label": "Pause lyd (Readspeaker)", + "default": "Pause lyd" + }, + { + "label": "Innholdsnavn for lyd", + "default": "Lyd" + }, + { + "label": "Melding vist når lydkilde ikke er støttet", + "default": "Nettleseren din støtter ikke denne lydkilden" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nl.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nl.json new file mode 100644 index 000000000..210916f68 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nl.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Bronbestanden" + }, + { + "label": "Spelermodus", + "description": "Selecteer de spelermodus.", + "options": [ + { + "label": "Minimalistisch" + }, + { + "label": "Volledig" + }, + { + "label": "Onzichtbaar" + } + ] + }, + { + "label": "Past in de wrapper" + }, + { + "label": "Activeer audiobediening", + "description": "De audiobediening stelt je in staat om bijvoorbeeld de audio te pauzeren." + }, + { + "label": "Activeer automatisch afspelen", + "description": "Met autoplay begint de audio direct met afspelen. Als autoplay is uitgeschakeld, dan dient de gebruiker de audio te starten met de playknop ." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audionaam", + "default": "Audio" + }, + { + "label": "Statusbericht niet ondersteunde audio", + "default": "Je browser ondersteunt deze audio-opname niet" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nn.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nn.json new file mode 100644 index 000000000..8bac4c6eb --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/nn.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Lydfiler" + }, + { + "label": "Avspiller modus", + "description": "Velg avspiller modus.", + "options": [ + { + "label": "Minimal" + }, + { + "label": "Full" + }, + { + "label": "Gjennomsiktig" + } + ] + }, + { + "label": "Tilpass størrelse" + }, + { + "label": "Slå på kontrollar", + "description": "Kontrollane lar mellom anna brukaren setje lydavspelinga på pause" + }, + { + "label": "Slå på automatisk avspeling", + "description": "Med automatisk avspeling begynner lyden å spele av seg sjølv med ein gang. Viss automatisk avspeling er slått av må brukaren trykkje for å starte avspelinga." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Innhaldsnamn for lyd", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pl.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pl.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pl.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt-br.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt-br.json new file mode 100644 index 000000000..39d9e3698 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt-br.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Arquivos fonte" + }, + { + "label": "Modo do reprodutor", + "description": "Selecione o layout do reprodutor.", + "options": [ + { + "label": "Minimalista" + }, + { + "label": "Completo" + }, + { + "label": "Transparente" + } + ] + }, + { + "label": "Preencher" + }, + { + "label": "Habilitar controles", + "description": "Controles permitem que o usuário por exemplo, pause o áudio" + }, + { + "label": "Habilitar reprodução automática", + "description": "O áudio é reproduzido automaticamente. Se a reprodução automática estiver desabilitada, o usuário deve pressionar o botão de reprodução para iniciar o áudio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Nome do conteúdo de áudio", + "default": "Áudio" + }, + { + "label": "Mensagem de áudio não suportado", + "default": "Seu navegador não suporta este áudio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/pt.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ro.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ro.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ro.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ru.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ru.json new file mode 100644 index 000000000..96561ce78 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/ru.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Исходный файл" + }, + { + "label": "Режим плеера", + "description": "Выберите макет плеера.", + "options": [ + { + "label": "Минималистичная эмблема" + }, + { + "label": "Полная звуковая дорожка" + }, + { + "label": "Прозрачный" + } + ] + }, + { + "label": "Расположить по всей горизонтали" + }, + { + "label": "Разрешить контроль", + "description": "Контроль позволит пользователю, например, поставить аудио на паузу" + }, + { + "label": "Разрешить автоматическое воспроизведение", + "description": "С автоматическим воспроизведением дорожка начнёт воспроизводиться сразу. Если автовоспроизведение запрещено, пользователю необходимо нажать кнопку воспроизведения самостоятельно." + }, + { + "label": "Воспроизвести аудио (ассистирующие технологии)", + "default": "Воспроизвести аудио" + }, + { + "label": "Пауза (ассистирующие технологии)", + "default": "Пауза" + }, + { + "label": "Название контента аудио", + "default": "Аудио" + }, + { + "label": "Сообщение об ошибке аудио", + "default": "Ваш браузер не поддерживает аудио" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sl.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sl.json new file mode 100644 index 000000000..d79fc6889 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sl.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Izvorne datoteke" + }, + { + "label": "Predvajalnik", + "description": "Nastavitev videza predvajalnika.", + "options": [ + { + "label": "Minimalističen" + }, + { + "label": "Celoten" + }, + { + "label": "Prosojno" + } + ] + }, + { + "label": "Prilagodi širini okna" + }, + { + "label": "Onemogoči kontrolnike v predvajalniku.", + "description": "Kontrolniki na primer uporabniku dovoljujejo začasno prekinitev predvajanja." + }, + { + "label": "Omogoči samodejno predvajanje", + "description": "Nastavitev samodejnega predvajanja. Neizbežno v primeru prosojnosti predvajalnika." + }, + { + "label": "Predvajaj zvok", + "default": "Predvajaj zvok" + }, + { + "label": "Zaustavi predvajanje", + "default": "Zaustavi predvajanje" + }, + { + "label": "Naslov vsebine posnetka", + "default": "Zvočni posnetek" + }, + { + "label": "Sporočilo o nepodprtosti zvočnega posnetka", + "default": "Spletni brskalnik ne podpira tega zvočnega posnetka." + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sma.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sma.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sma.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sme.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sme.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sme.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/smj.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/smj.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/smj.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sr.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sr.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sr.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sv.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sv.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/sv.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/tr.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/tr.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/tr.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/vi.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/vi.json new file mode 100644 index 000000000..488fc2c89 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/vi.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "Source files" + }, + { + "label": "Player mode", + "description": "Select the layout of the player.", + "options": [ + { + "label": "Minimalistic" + }, + { + "label": "Full" + }, + { + "label": "Transparent" + } + ] + }, + { + "label": "Fit to wrapper" + }, + { + "label": "Enable controls", + "description": "Controls allow the user to for instance pause the audio" + }, + { + "label": "Enable autoplay", + "description": "With autoplay the audio starts to play immediately. If autoplay is disabled the user presses a play button to start the audio." + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "Audio content name", + "default": "Audio" + }, + { + "label": "Audio not supported message", + "default": "Your browser does not support this audio" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hans.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hans.json new file mode 100644 index 000000000..044b1cef3 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hans.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "来源档案" + }, + { + "label": "播放方式", + "description": "选择播放器面板。", + "options": [ + { + "label": "简约" + }, + { + "label": "完整" + }, + { + "label": "透明" + } + ] + }, + { + "label": "自适应画面" + }, + { + "label": "启用控制项", + "description": "允许用户控制音频的播放,像是暂停。" + }, + { + "label": "启用自动播放", + "description": "让画面载入后就开始播放,如果停用,使用者就必须点击播放按钮才能开始。" + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "音乐名称", + "default": "Audio" + }, + { + "label": "无法播放时显示讯息", + "default": "你的浏览器不支持播放这个格式的音乐档案。" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hant.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hant.json new file mode 100644 index 000000000..379b083df --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh-hant.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "來源檔案" + }, + { + "label": "播放方式", + "description": "選擇播放器面版。", + "options": [ + { + "label": "簡約" + }, + { + "label": "完整" + }, + { + "label": "透明" + } + ] + }, + { + "label": "自適應畫面" + }, + { + "label": "啟用控制項", + "description": "允許用戶控制音頻的播放,像是暫停。" + }, + { + "label": "啟用自動播放", + "description": "讓畫面載入後就開始播放,如果停用,使用者就必須點擊播放按鈕才能開始。" + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "音樂名稱", + "default": "Audio" + }, + { + "label": "無法播放時顯示訊息", + "default": "你的瀏覽器不支援播放這個格式的音樂檔案。" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh.json new file mode 100644 index 000000000..379b083df --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/language/zh.json @@ -0,0 +1,49 @@ +{ + "semantics": [ + { + "label": "來源檔案" + }, + { + "label": "播放方式", + "description": "選擇播放器面版。", + "options": [ + { + "label": "簡約" + }, + { + "label": "完整" + }, + { + "label": "透明" + } + ] + }, + { + "label": "自適應畫面" + }, + { + "label": "啟用控制項", + "description": "允許用戶控制音頻的播放,像是暫停。" + }, + { + "label": "啟用自動播放", + "description": "讓畫面載入後就開始播放,如果停用,使用者就必須點擊播放按鈕才能開始。" + }, + { + "label": "Play audio (Readspeaker)", + "default": "Play audio" + }, + { + "label": "Pause audio (Readspeaker)", + "default": "Pause audio" + }, + { + "label": "音樂名稱", + "default": "Audio" + }, + { + "label": "無法播放時顯示訊息", + "default": "你的瀏覽器不支援播放這個格式的音樂檔案。" + } + ] +} diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/library.json b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/library.json new file mode 100644 index 000000000..d49d72705 --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/library.json @@ -0,0 +1,40 @@ +{ + "title": "Audio", + "contentType": "Media", + "description": "Simple library that displays an audio player.", + "majorVersion": 1, + "minorVersion": 4, + "patchVersion": 3, + "runnable": 1, + "machineName": "H5P.Audio", + "embedTypes": [ + "iframe" + ], + "author": "Joubel", + "coreApi": { + "majorVersion": 1, + "minorVersion": 19 + }, + "preloadedJs": [ + { + "path": "scripts/audio.js" + } + ], + "preloadedCss": [ + { + "path": "styles/audio.css" + } + ], + "preloadedDependencies": [ + { + "machineName": "flowplayer", + "majorVersion": 1, + "minorVersion": 0 + }, + { + "machineName": "FontAwesome", + "majorVersion": 4, + "minorVersion": 5 + } + ] +} \ No newline at end of file diff --git a/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/scripts/audio.js b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/scripts/audio.js new file mode 100644 index 000000000..a9a2985de --- /dev/null +++ b/plugin/h5p/cache-h5p/launch/source-dialogcard/H5P.Audio-1.4/scripts/audio.js @@ -0,0 +1,386 @@ +var H5P = H5P || {}; + +/** + * H5P audio module + * + * @external {jQuery} $ H5P.jQuery + */ +H5P.Audio = (function ($) { + /** + * @param {Object} params Options for this library. + * @param {Number} id Content identifier. + * @param {Object} extras Extras. + * @returns {undefined} + */ + function C(params, id, extras) { + H5P.EventDispatcher.call(this); + this.contentId = id; + this.params = params; + this.extras = extras; + + this.toggleButtonEnabled = true; + + // Retrieve previous state + if (extras && extras.previousState !== undefined) { + this.oldTime = extras.previousState.currentTime; + } + + this.params = $.extend({}, { + playerMode: 'minimalistic', + fitToWrapper: false, + controls: true, + autoplay: false, + audioNotSupported: "Your browser does not support this audio", + playAudio: "Play audio", + pauseAudio: "Pause audio" + }, params); + + this.on('resize', this.resize, this); + } + + C.prototype = Object.create(H5P.EventDispatcher.prototype); + C.prototype.constructor = C; + + /** + * Adds a minimalistic audio player with only "play" and "pause" functionality. + * + * @param {jQuery} $container Container for the player. + * @param {boolean} transparentMode true: the player is only visible when hovering over it; false: player's UI always visible + */ + C.prototype.addMinimalAudioPlayer = function ($container, transparentMode) { + var INNER_CONTAINER = 'h5p-audio-inner'; + var AUDIO_BUTTON = 'h5p-audio-minimal-button'; + var PLAY_BUTTON = 'h5p-audio-minimal-play'; + var PLAY_BUTTON_PAUSED = 'h5p-audio-minimal-play-paused'; + var PAUSE_BUTTON = 'h5p-audio-minimal-pause'; + + var self = this; + this.$container = $container; + + self.$inner = $('
    ', { + 'class': INNER_CONTAINER + (transparentMode ? ' h5p-audio-transparent' : '') + }).appendTo($container); + + var audioButton = $('") + .button({ + label: this.options.closeText, + icons: { + primary: "ui-icon-closethick" + }, + text: false + }) + .addClass("ui-dialog-titlebar-close") + .appendTo( this.uiDialogTitlebar ); + this._on( this.uiDialogTitlebarClose, { + click: function( event ) { + event.preventDefault(); + this.close( event ); + } + }); + + uiDialogTitle = $("") + .uniqueId() + .addClass("ui-dialog-title") + .prependTo( this.uiDialogTitlebar ); + this._title( uiDialogTitle ); + + this.uiDialog.attr({ + "aria-labelledby": uiDialogTitle.attr("id") + }); + }, + + _title: function( title ) { + if ( !this.options.title ) { + title.html(" "); + } + title.text( this.options.title ); + }, + + _createButtonPane: function() { + this.uiDialogButtonPane = $("
    ") + .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"); + + this.uiButtonSet = $("
    ") + .addClass("ui-dialog-buttonset") + .appendTo( this.uiDialogButtonPane ); + + this._createButtons(); + }, + + _createButtons: function() { + var that = this, + buttons = this.options.buttons; + + // if we already have a button pane, remove it + this.uiDialogButtonPane.remove(); + this.uiButtonSet.empty(); + + if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) { + this.uiDialog.removeClass("ui-dialog-buttons"); + return; + } + + $.each( buttons, function( name, props ) { + var click, buttonOptions; + props = $.isFunction( props ) ? + { click: props, text: name } : + props; + // Default to a non-submitting button + props = $.extend( { type: "button" }, props ); + // Change the context for the click callback to be the main element + click = props.click; + props.click = function() { + click.apply( that.element[0], arguments ); + }; + buttonOptions = { + icons: props.icons, + text: props.showText + }; + delete props.icons; + delete props.showText; + $( "", props ) + .button( buttonOptions ) + .appendTo( that.uiButtonSet ); + }); + this.uiDialog.addClass("ui-dialog-buttons"); + this.uiDialogButtonPane.appendTo( this.uiDialog ); + }, + + _makeDraggable: function() { + var that = this, + options = this.options; + + function filteredUi( ui ) { + return { + position: ui.position, + offset: ui.offset + }; + } + + this.uiDialog.draggable({ + cancel: ".ui-dialog-content, .ui-dialog-titlebar-close", + handle: ".ui-dialog-titlebar", + containment: "document", + start: function( event, ui ) { + $( this ).addClass("ui-dialog-dragging"); + that._blockFrames(); + that._trigger( "dragStart", event, filteredUi( ui ) ); + }, + drag: function( event, ui ) { + that._trigger( "drag", event, filteredUi( ui ) ); + }, + stop: function( event, ui ) { + options.position = [ + ui.position.left - that.document.scrollLeft(), + ui.position.top - that.document.scrollTop() + ]; + $( this ).removeClass("ui-dialog-dragging"); + that._unblockFrames(); + that._trigger( "dragStop", event, filteredUi( ui ) ); + } + }); + }, + + _makeResizable: function() { + var that = this, + options = this.options, + handles = options.resizable, + // .ui-resizable has position: relative defined in the stylesheet + // but dialogs have to use absolute or fixed positioning + position = this.uiDialog.css("position"), + resizeHandles = typeof handles === "string" ? + handles : + "n,e,s,w,se,sw,ne,nw"; + + function filteredUi( ui ) { + return { + originalPosition: ui.originalPosition, + originalSize: ui.originalSize, + position: ui.position, + size: ui.size + }; + } + + this.uiDialog.resizable({ + cancel: ".ui-dialog-content", + containment: "document", + alsoResize: this.element, + maxWidth: options.maxWidth, + maxHeight: options.maxHeight, + minWidth: options.minWidth, + minHeight: this._minHeight(), + handles: resizeHandles, + start: function( event, ui ) { + $( this ).addClass("ui-dialog-resizing"); + that._blockFrames(); + that._trigger( "resizeStart", event, filteredUi( ui ) ); + }, + resize: function( event, ui ) { + that._trigger( "resize", event, filteredUi( ui ) ); + }, + stop: function( event, ui ) { + options.height = $( this ).height(); + options.width = $( this ).width(); + $( this ).removeClass("ui-dialog-resizing"); + that._unblockFrames(); + that._trigger( "resizeStop", event, filteredUi( ui ) ); + } + }) + .css( "position", position ); + }, + + _minHeight: function() { + var options = this.options; + + return options.height === "auto" ? + options.minHeight : + Math.min( options.minHeight, options.height ); + }, + + _position: function() { + // Need to show the dialog to get the actual offset in the position plugin + var isVisible = this.uiDialog.is(":visible"); + if ( !isVisible ) { + this.uiDialog.show(); + } + this.uiDialog.position( this.options.position ); + if ( !isVisible ) { + this.uiDialog.hide(); + } + }, + + _setOptions: function( options ) { + var that = this, + resize = false, + resizableOptions = {}; + + $.each( options, function( key, value ) { + that._setOption( key, value ); + + if ( key in sizeRelatedOptions ) { + resize = true; + } + if ( key in resizableRelatedOptions ) { + resizableOptions[ key ] = value; + } + }); + + if ( resize ) { + this._size(); + this._position(); + } + if ( this.uiDialog.is(":data(ui-resizable)") ) { + this.uiDialog.resizable( "option", resizableOptions ); + } + }, + + _setOption: function( key, value ) { + /*jshint maxcomplexity:15*/ + var isDraggable, isResizable, + uiDialog = this.uiDialog; + + if ( key === "dialogClass" ) { + uiDialog + .removeClass( this.options.dialogClass ) + .addClass( value ); + } + + if ( key === "disabled" ) { + return; + } + + this._super( key, value ); + + if ( key === "appendTo" ) { + this.uiDialog.appendTo( this._appendTo() ); + } + + if ( key === "buttons" ) { + this._createButtons(); + } + + if ( key === "closeText" ) { + this.uiDialogTitlebarClose.button({ + // Ensure that we always pass a string + label: "" + value + }); + } + + if ( key === "draggable" ) { + isDraggable = uiDialog.is(":data(ui-draggable)"); + if ( isDraggable && !value ) { + uiDialog.draggable("destroy"); + } + + if ( !isDraggable && value ) { + this._makeDraggable(); + } + } + + if ( key === "position" ) { + this._position(); + } + + if ( key === "resizable" ) { + // currently resizable, becoming non-resizable + isResizable = uiDialog.is(":data(ui-resizable)"); + if ( isResizable && !value ) { + uiDialog.resizable("destroy"); + } + + // currently resizable, changing handles + if ( isResizable && typeof value === "string" ) { + uiDialog.resizable( "option", "handles", value ); + } + + // currently non-resizable, becoming resizable + if ( !isResizable && value !== false ) { + this._makeResizable(); + } + } + + if ( key === "title" ) { + this._title( this.uiDialogTitlebar.find(".ui-dialog-title") ); + } + }, + + _size: function() { + // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content + // divs will both have width and height set, so we need to reset them + var nonContentHeight, minContentHeight, maxContentHeight, + options = this.options; + + // Reset content sizing + this.element.show().css({ + width: "auto", + minHeight: 0, + maxHeight: "none", + height: 0 + }); + + if ( options.minWidth > options.width ) { + options.width = options.minWidth; + } + + // reset wrapper sizing + // determine the height of all the non-content elements + nonContentHeight = this.uiDialog.css({ + height: "auto", + width: options.width + }) + .outerHeight(); + minContentHeight = Math.max( 0, options.minHeight - nonContentHeight ); + maxContentHeight = typeof options.maxHeight === "number" ? + Math.max( 0, options.maxHeight - nonContentHeight ) : + "none"; + + if ( options.height === "auto" ) { + this.element.css({ + minHeight: minContentHeight, + maxHeight: maxContentHeight, + height: "auto" + }); + } else { + this.element.height( Math.max( 0, options.height - nonContentHeight ) ); + } + + if (this.uiDialog.is(":data(ui-resizable)") ) { + this.uiDialog.resizable( "option", "minHeight", this._minHeight() ); + } + }, + + _blockFrames: function() { + this.iframeBlocks = this.document.find( "iframe" ).map(function() { + var iframe = $( this ); + + return $( "
    " ) + .css({ + position: "absolute", + width: iframe.outerWidth(), + height: iframe.outerHeight() + }) + .appendTo( iframe.parent() ) + .offset( iframe.offset() )[0]; + }); + }, + + _unblockFrames: function() { + if ( this.iframeBlocks ) { + this.iframeBlocks.remove(); + delete this.iframeBlocks; + } + }, + + _allowInteraction: function( event ) { + if ( $( event.target ).closest(".ui-dialog").length ) { + return true; + } + + // TODO: Remove hack when datepicker implements + // the .ui-front logic (#8989) + return !!$( event.target ).closest(".ui-datepicker").length; + }, + + _createOverlay: function() { + if ( !this.options.modal ) { + return; + } + + var that = this, + widgetFullName = this.widgetFullName; + if ( !$.ui.dialog.overlayInstances ) { + // Prevent use of anchors and inputs. + // We use a delay in case the overlay is created from an + // event that we're going to be cancelling. (#2804) + this._delay(function() { + // Handle .dialog().dialog("close") (#4065) + if ( $.ui.dialog.overlayInstances ) { + this.document.bind( "focusin.dialog", function( event ) { + if ( !that._allowInteraction( event ) ) { + event.preventDefault(); + $(".ui-dialog:visible:last .ui-dialog-content") + .data( widgetFullName )._focusTabbable(); + } + }); + } + }); + } + + this.overlay = $("
    ") + .addClass("ui-widget-overlay ui-front") + .appendTo( this._appendTo() ); + this._on( this.overlay, { + mousedown: "_keepFocus" + }); + $.ui.dialog.overlayInstances++; + }, + + _destroyOverlay: function() { + if ( !this.options.modal ) { + return; + } + + if ( this.overlay ) { + $.ui.dialog.overlayInstances--; + + if ( !$.ui.dialog.overlayInstances ) { + this.document.unbind( "focusin.dialog" ); + } + this.overlay.remove(); + this.overlay = null; + } + } +}); + +$.ui.dialog.overlayInstances = 0; + +// DEPRECATED +if ( $.uiBackCompat !== false ) { + // position option with array notation + // just override with old implementation + $.widget( "ui.dialog", $.ui.dialog, { + _position: function() { + var position = this.options.position, + myAt = [], + offset = [ 0, 0 ], + isVisible; + + if ( position ) { + if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) { + myAt = position.split ? position.split(" ") : [ position[0], position[1] ]; + if ( myAt.length === 1 ) { + myAt[1] = myAt[0]; + } + + $.each( [ "left", "top" ], function( i, offsetPosition ) { + if ( +myAt[ i ] === myAt[ i ] ) { + offset[ i ] = myAt[ i ]; + myAt[ i ] = offsetPosition; + } + }); + + position = { + my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " + + myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]), + at: myAt.join(" ") + }; + } + + position = $.extend( {}, $.ui.dialog.prototype.options.position, position ); + } else { + position = $.ui.dialog.prototype.options.position; + } + + // need to show the dialog to get the actual offset in the position plugin + isVisible = this.uiDialog.is(":visible"); + if ( !isVisible ) { + this.uiDialog.show(); + } + this.uiDialog.position( position ); + if ( !isVisible ) { + this.uiDialog.hide(); + } + } + }); +} + +}( jQuery ) ); + +(function( $, undefined ) { + +var rvertical = /up|down|vertical/, + rpositivemotion = /up|left|vertical|horizontal/; + +$.effects.effect.blind = function( o, done ) { + // Create element + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "hide" ), + direction = o.direction || "up", + vertical = rvertical.test( direction ), + ref = vertical ? "height" : "width", + ref2 = vertical ? "top" : "left", + motion = rpositivemotion.test( direction ), + animation = {}, + show = mode === "show", + wrapper, distance, margin; + + // if already wrapped, the wrapper's properties are my property. #6245 + if ( el.parent().is( ".ui-effects-wrapper" ) ) { + $.effects.save( el.parent(), props ); + } else { + $.effects.save( el, props ); + } + el.show(); + wrapper = $.effects.createWrapper( el ).css({ + overflow: "hidden" + }); + + distance = wrapper[ ref ](); + margin = parseFloat( wrapper.css( ref2 ) ) || 0; + + animation[ ref ] = show ? distance : 0; + if ( !motion ) { + el + .css( vertical ? "bottom" : "right", 0 ) + .css( vertical ? "top" : "left", "auto" ) + .css({ position: "absolute" }); + + animation[ ref2 ] = show ? margin : distance + margin; + } + + // start at 0 if we are showing + if ( show ) { + wrapper.css( ref, 0 ); + if ( ! motion ) { + wrapper.css( ref2, margin + distance ); + } + } + + // Animate + wrapper.animate( animation, { + duration: o.duration, + easing: o.easing, + queue: false, + complete: function() { + if ( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + } + }); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.bounce = function( o, done ) { + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + + // defaults: + mode = $.effects.setMode( el, o.mode || "effect" ), + hide = mode === "hide", + show = mode === "show", + direction = o.direction || "up", + distance = o.distance, + times = o.times || 5, + + // number of internal animations + anims = times * 2 + ( show || hide ? 1 : 0 ), + speed = o.duration / anims, + easing = o.easing, + + // utility: + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + motion = ( direction === "up" || direction === "left" ), + i, + upAnim, + downAnim, + + // we will need to re-assemble the queue to stack our animations in place + queue = el.queue(), + queuelen = queue.length; + + // Avoid touching opacity to prevent clearType and PNG issues in IE + if ( show || hide ) { + props.push( "opacity" ); + } + + $.effects.save( el, props ); + el.show(); + $.effects.createWrapper( el ); // Create Wrapper + + // default distance for the BIGGEST bounce is the outer Distance / 3 + if ( !distance ) { + distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3; + } + + if ( show ) { + downAnim = { opacity: 1 }; + downAnim[ ref ] = 0; + + // if we are showing, force opacity 0 and set the initial position + // then do the "first" animation + el.css( "opacity", 0 ) + .css( ref, motion ? -distance * 2 : distance * 2 ) + .animate( downAnim, speed, easing ); + } + + // start at the smallest distance if we are hiding + if ( hide ) { + distance = distance / Math.pow( 2, times - 1 ); + } + + downAnim = {}; + downAnim[ ref ] = 0; + // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here + for ( i = 0; i < times; i++ ) { + upAnim = {}; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + el.animate( upAnim, speed, easing ) + .animate( downAnim, speed, easing ); + + distance = hide ? distance * 2 : distance / 2; + } + + // Last Bounce when Hiding + if ( hide ) { + upAnim = { opacity: 0 }; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + el.animate( upAnim, speed, easing ); + } + + el.queue(function() { + if ( hide ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + }); + + // inject all the animations we just queued to be first in line (after "inprogress") + if ( queuelen > 1) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); + } + el.dequeue(); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.clip = function( o, done ) { + // Create element + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "hide" ), + show = mode === "show", + direction = o.direction || "vertical", + vert = direction === "vertical", + size = vert ? "height" : "width", + position = vert ? "top" : "left", + animation = {}, + wrapper, animate, distance; + + // Save & Show + $.effects.save( el, props ); + el.show(); + + // Create Wrapper + wrapper = $.effects.createWrapper( el ).css({ + overflow: "hidden" + }); + animate = ( el[0].tagName === "IMG" ) ? wrapper : el; + distance = animate[ size ](); + + // Shift + if ( show ) { + animate.css( size, 0 ); + animate.css( position, distance / 2 ); + } + + // Create Animation Object: + animation[ size ] = show ? distance : 0; + animation[ position ] = show ? 0 : distance / 2; + + // Animate + animate.animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( !show ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + } + }); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.drop = function( o, done ) { + + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "hide" ), + show = mode === "show", + direction = o.direction || "left", + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg", + animation = { + opacity: show ? 1 : 0 + }, + distance; + + // Adjust + $.effects.save( el, props ); + el.show(); + $.effects.createWrapper( el ); + + distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2; + + if ( show ) { + el + .css( "opacity", 0 ) + .css( ref, motion === "pos" ? -distance : distance ); + } + + // Animation + animation[ ref ] = ( show ? + ( motion === "pos" ? "+=" : "-=" ) : + ( motion === "pos" ? "-=" : "+=" ) ) + + distance; + + // Animate + el.animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + } + }); +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.explode = function( o, done ) { + + var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3, + cells = rows, + el = $( this ), + mode = $.effects.setMode( el, o.mode || "hide" ), + show = mode === "show", + + // show and then visibility:hidden the element before calculating offset + offset = el.show().css( "visibility", "hidden" ).offset(), + + // width and height of a piece + width = Math.ceil( el.outerWidth() / cells ), + height = Math.ceil( el.outerHeight() / rows ), + pieces = [], + + // loop + i, j, left, top, mx, my; + + // children animate complete: + function childComplete() { + pieces.push( this ); + if ( pieces.length === rows * cells ) { + animComplete(); + } + } + + // clone the element for each row and cell. + for( i = 0; i < rows ; i++ ) { // ===> + top = offset.top + i * height; + my = i - ( rows - 1 ) / 2 ; + + for( j = 0; j < cells ; j++ ) { // ||| + left = offset.left + j * width; + mx = j - ( cells - 1 ) / 2 ; + + // Create a clone of the now hidden main element that will be absolute positioned + // within a wrapper div off the -left and -top equal to size of our pieces + el + .clone() + .appendTo( "body" ) + .wrap( "
    " ) + .css({ + position: "absolute", + visibility: "visible", + left: -j * width, + top: -i * height + }) + + // select the wrapper - make it overflow: hidden and absolute positioned based on + // where the original was located +left and +top equal to the size of pieces + .parent() + .addClass( "ui-effects-explode" ) + .css({ + position: "absolute", + overflow: "hidden", + width: width, + height: height, + left: left + ( show ? mx * width : 0 ), + top: top + ( show ? my * height : 0 ), + opacity: show ? 0 : 1 + }).animate({ + left: left + ( show ? 0 : mx * width ), + top: top + ( show ? 0 : my * height ), + opacity: show ? 1 : 0 + }, o.duration || 500, o.easing, childComplete ); + } + } + + function animComplete() { + el.css({ + visibility: "visible" + }); + $( pieces ).remove(); + if ( !show ) { + el.hide(); + } + done(); + } +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.fade = function( o, done ) { + var el = $( this ), + mode = $.effects.setMode( el, o.mode || "toggle" ); + + el.animate({ + opacity: mode + }, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: done + }); +}; + +})( jQuery ); + +(function( $, undefined ) { + +$.effects.effect.fold = function( o, done ) { + + // Create element + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "hide" ), + show = mode === "show", + hide = mode === "hide", + size = o.size || 15, + percent = /([0-9]+)%/.exec( size ), + horizFirst = !!o.horizFirst, + widthFirst = show !== horizFirst, + ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ], + duration = o.duration / 2, + wrapper, distance, + animation1 = {}, + animation2 = {}; + + $.effects.save( el, props ); + el.show(); + + // Create Wrapper + wrapper = $.effects.createWrapper( el ).css({ + overflow: "hidden" + }); + distance = widthFirst ? + [ wrapper.width(), wrapper.height() ] : + [ wrapper.height(), wrapper.width() ]; + + if ( percent ) { + size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ]; + } + if ( show ) { + wrapper.css( horizFirst ? { + height: 0, + width: size + } : { + height: size, + width: 0 + }); + } + + // Animation + animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size; + animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0; + + // Animate + wrapper + .animate( animation1, duration, o.easing ) + .animate( animation2, duration, o.easing, function() { + if ( hide ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + }); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.highlight = function( o, done ) { + var elem = $( this ), + props = [ "backgroundImage", "backgroundColor", "opacity" ], + mode = $.effects.setMode( elem, o.mode || "show" ), + animation = { + backgroundColor: elem.css( "backgroundColor" ) + }; + + if (mode === "hide") { + animation.opacity = 0; + } + + $.effects.save( elem, props ); + + elem + .show() + .css({ + backgroundImage: "none", + backgroundColor: o.color || "#ffff99" + }) + .animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( mode === "hide" ) { + elem.hide(); + } + $.effects.restore( elem, props ); + done(); + } + }); +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.pulsate = function( o, done ) { + var elem = $( this ), + mode = $.effects.setMode( elem, o.mode || "show" ), + show = mode === "show", + hide = mode === "hide", + showhide = ( show || mode === "hide" ), + + // showing or hiding leaves of the "last" animation + anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ), + duration = o.duration / anims, + animateTo = 0, + queue = elem.queue(), + queuelen = queue.length, + i; + + if ( show || !elem.is(":visible")) { + elem.css( "opacity", 0 ).show(); + animateTo = 1; + } + + // anims - 1 opacity "toggles" + for ( i = 1; i < anims; i++ ) { + elem.animate({ + opacity: animateTo + }, duration, o.easing ); + animateTo = 1 - animateTo; + } + + elem.animate({ + opacity: animateTo + }, duration, o.easing); + + elem.queue(function() { + if ( hide ) { + elem.hide(); + } + done(); + }); + + // We just queued up "anims" animations, we need to put them next in the queue + if ( queuelen > 1 ) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); + } + elem.dequeue(); +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.puff = function( o, done ) { + var elem = $( this ), + mode = $.effects.setMode( elem, o.mode || "hide" ), + hide = mode === "hide", + percent = parseInt( o.percent, 10 ) || 150, + factor = percent / 100, + original = { + height: elem.height(), + width: elem.width(), + outerHeight: elem.outerHeight(), + outerWidth: elem.outerWidth() + }; + + $.extend( o, { + effect: "scale", + queue: false, + fade: true, + mode: mode, + complete: done, + percent: hide ? percent : 100, + from: hide ? + original : + { + height: original.height * factor, + width: original.width * factor, + outerHeight: original.outerHeight * factor, + outerWidth: original.outerWidth * factor + } + }); + + elem.effect( o ); +}; + +$.effects.effect.scale = function( o, done ) { + + // Create element + var el = $( this ), + options = $.extend( true, {}, o ), + mode = $.effects.setMode( el, o.mode || "effect" ), + percent = parseInt( o.percent, 10 ) || + ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ), + direction = o.direction || "both", + origin = o.origin, + original = { + height: el.height(), + width: el.width(), + outerHeight: el.outerHeight(), + outerWidth: el.outerWidth() + }, + factor = { + y: direction !== "horizontal" ? (percent / 100) : 1, + x: direction !== "vertical" ? (percent / 100) : 1 + }; + + // We are going to pass this effect to the size effect: + options.effect = "size"; + options.queue = false; + options.complete = done; + + // Set default origin and restore for show/hide + if ( mode !== "effect" ) { + options.origin = origin || ["middle","center"]; + options.restore = true; + } + + options.from = o.from || ( mode === "show" ? { + height: 0, + width: 0, + outerHeight: 0, + outerWidth: 0 + } : original ); + options.to = { + height: original.height * factor.y, + width: original.width * factor.x, + outerHeight: original.outerHeight * factor.y, + outerWidth: original.outerWidth * factor.x + }; + + // Fade option to support puff + if ( options.fade ) { + if ( mode === "show" ) { + options.from.opacity = 0; + options.to.opacity = 1; + } + if ( mode === "hide" ) { + options.from.opacity = 1; + options.to.opacity = 0; + } + } + + // Animate + el.effect( options ); + +}; + +$.effects.effect.size = function( o, done ) { + + // Create element + var original, baseline, factor, + el = $( this ), + props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ], + + // Always restore + props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ], + + // Copy for children + props2 = [ "width", "height", "overflow" ], + cProps = [ "fontSize" ], + vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ], + hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ], + + // Set options + mode = $.effects.setMode( el, o.mode || "effect" ), + restore = o.restore || mode !== "effect", + scale = o.scale || "both", + origin = o.origin || [ "middle", "center" ], + position = el.css( "position" ), + props = restore ? props0 : props1, + zero = { + height: 0, + width: 0, + outerHeight: 0, + outerWidth: 0 + }; + + if ( mode === "show" ) { + el.show(); + } + original = { + height: el.height(), + width: el.width(), + outerHeight: el.outerHeight(), + outerWidth: el.outerWidth() + }; + + if ( o.mode === "toggle" && mode === "show" ) { + el.from = o.to || zero; + el.to = o.from || original; + } else { + el.from = o.from || ( mode === "show" ? zero : original ); + el.to = o.to || ( mode === "hide" ? zero : original ); + } + + // Set scaling factor + factor = { + from: { + y: el.from.height / original.height, + x: el.from.width / original.width + }, + to: { + y: el.to.height / original.height, + x: el.to.width / original.width + } + }; + + // Scale the css box + if ( scale === "box" || scale === "both" ) { + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + props = props.concat( vProps ); + el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from ); + el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to ); + } + + // Horizontal props scaling + if ( factor.from.x !== factor.to.x ) { + props = props.concat( hProps ); + el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from ); + el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to ); + } + } + + // Scale the content + if ( scale === "content" || scale === "both" ) { + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + props = props.concat( cProps ).concat( props2 ); + el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from ); + el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to ); + } + } + + $.effects.save( el, props ); + el.show(); + $.effects.createWrapper( el ); + el.css( "overflow", "hidden" ).css( el.from ); + + // Adjust + if (origin) { // Calculate baseline shifts + baseline = $.effects.getBaseline( origin, original ); + el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y; + el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x; + el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y; + el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x; + } + el.css( el.from ); // set top & left + + // Animate + if ( scale === "content" || scale === "both" ) { // Scale the children + + // Add margins/font-size + vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps); + hProps = hProps.concat([ "marginLeft", "marginRight" ]); + props2 = props0.concat(vProps).concat(hProps); + + el.find( "*[width]" ).each( function(){ + var child = $( this ), + c_original = { + height: child.height(), + width: child.width(), + outerHeight: child.outerHeight(), + outerWidth: child.outerWidth() + }; + if (restore) { + $.effects.save(child, props2); + } + + child.from = { + height: c_original.height * factor.from.y, + width: c_original.width * factor.from.x, + outerHeight: c_original.outerHeight * factor.from.y, + outerWidth: c_original.outerWidth * factor.from.x + }; + child.to = { + height: c_original.height * factor.to.y, + width: c_original.width * factor.to.x, + outerHeight: c_original.height * factor.to.y, + outerWidth: c_original.width * factor.to.x + }; + + // Vertical props scaling + if ( factor.from.y !== factor.to.y ) { + child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from ); + child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to ); + } + + // Horizontal props scaling + if ( factor.from.x !== factor.to.x ) { + child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from ); + child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to ); + } + + // Animate children + child.css( child.from ); + child.animate( child.to, o.duration, o.easing, function() { + + // Restore children + if ( restore ) { + $.effects.restore( child, props2 ); + } + }); + }); + } + + // Animate + el.animate( el.to, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( el.to.opacity === 0 ) { + el.css( "opacity", el.from.opacity ); + } + if( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + if ( !restore ) { + + // we need to calculate our new positioning based on the scaling + if ( position === "static" ) { + el.css({ + position: "relative", + top: el.to.top, + left: el.to.left + }); + } else { + $.each([ "top", "left" ], function( idx, pos ) { + el.css( pos, function( _, str ) { + var val = parseInt( str, 10 ), + toRef = idx ? el.to.left : el.to.top; + + // if original was "auto", recalculate the new value from wrapper + if ( str === "auto" ) { + return toRef + "px"; + } + + return val + toRef + "px"; + }); + }); + } + } + + $.effects.removeWrapper( el ); + done(); + } + }); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.shake = function( o, done ) { + + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], + mode = $.effects.setMode( el, o.mode || "effect" ), + direction = o.direction || "left", + distance = o.distance || 20, + times = o.times || 3, + anims = times * 2 + 1, + speed = Math.round(o.duration/anims), + ref = (direction === "up" || direction === "down") ? "top" : "left", + positiveMotion = (direction === "up" || direction === "left"), + animation = {}, + animation1 = {}, + animation2 = {}, + i, + + // we will need to re-assemble the queue to stack our animations in place + queue = el.queue(), + queuelen = queue.length; + + $.effects.save( el, props ); + el.show(); + $.effects.createWrapper( el ); + + // Animation + animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance; + animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2; + animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2; + + // Animate + el.animate( animation, speed, o.easing ); + + // Shakes + for ( i = 1; i < times; i++ ) { + el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing ); + } + el + .animate( animation1, speed, o.easing ) + .animate( animation, speed / 2, o.easing ) + .queue(function() { + if ( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + }); + + // inject all the animations we just queued to be first in line (after "inprogress") + if ( queuelen > 1) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); + } + el.dequeue(); + +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.slide = function( o, done ) { + + // Create element + var el = $( this ), + props = [ "position", "top", "bottom", "left", "right", "width", "height" ], + mode = $.effects.setMode( el, o.mode || "show" ), + show = mode === "show", + direction = o.direction || "left", + ref = (direction === "up" || direction === "down") ? "top" : "left", + positiveMotion = (direction === "up" || direction === "left"), + distance, + animation = {}; + + // Adjust + $.effects.save( el, props ); + el.show(); + distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ); + + $.effects.createWrapper( el ).css({ + overflow: "hidden" + }); + + if ( show ) { + el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance ); + } + + // Animation + animation[ ref ] = ( show ? + ( positiveMotion ? "+=" : "-=") : + ( positiveMotion ? "-=" : "+=")) + + distance; + + // Animate + el.animate( animation, { + queue: false, + duration: o.duration, + easing: o.easing, + complete: function() { + if ( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + done(); + } + }); +}; + +})(jQuery); + +(function( $, undefined ) { + +$.effects.effect.transfer = function( o, done ) { + var elem = $( this ), + target = $( o.to ), + targetFixed = target.css( "position" ) === "fixed", + body = $("body"), + fixTop = targetFixed ? body.scrollTop() : 0, + fixLeft = targetFixed ? body.scrollLeft() : 0, + endPosition = target.offset(), + animation = { + top: endPosition.top - fixTop , + left: endPosition.left - fixLeft , + height: target.innerHeight(), + width: target.innerWidth() + }, + startPosition = elem.offset(), + transfer = $( "
    " ) + .appendTo( document.body ) + .addClass( o.className ) + .css({ + top: startPosition.top - fixTop , + left: startPosition.left - fixLeft , + height: elem.innerHeight(), + width: elem.innerWidth(), + position: targetFixed ? "fixed" : "absolute" + }) + .animate( animation, o.duration, o.easing, function() { + transfer.remove(); + done(); + }); +}; + +})(jQuery); + +(function( $, undefined ) { + +$.widget( "ui.menu", { + version: "1.10.2", + defaultElement: "
    + {{ tool.name }} + {% if not is_child %} +
    {{ tool.versionName }} + {% endif %} +
    {{ tool.launchUrl }} + {% if tool.course is not empty %} + {{ tool.course.title }} + {% endif %} + {% if tool.session is not empty %} + ({{ tool.session.name }}) + {% endif %} + + {% if not is_child and tool.version == 'lti1p3' %} + + {{ 'webservices.png'|img(22, 'ConfigSettingsForTool'|get_plugin_lang('ImsLtiPlugin')) }} + + {% endif %} + + {% if not is_child %} + + {{ 'multiplicate_survey.png'|img(22, 'AddInCourses'|get_plugin_lang('ImsLtiPlugin')) }} + + {% endif %} + + {% if not is_child %} + + {{ 'session.png'|img(22, 'AddInSessions'|get_plugin_lang('ImsLtiPlugin')) }} + + {% endif %} + + {% if is_child %} + {% for category in categories %} + {% set url_eval_params = null %} + {% if tool.session is not empty %} + {% if category.get_course_code == tool.course.code and category.get_session_id == tool.session.id %} + {% set url_eval_params = {'selectcat': category.get_id, 'cidReq': tool.course.code, 'id_session': tool.session.id, 'gidReq': 0, 'gradebook': 0}|url_encode() %} + {% endif %} + {% else %} + {% if category.get_course_code == tool.course.code %} + {% set url_eval_params = {'selectcat': category.get_id, 'cidReq': tool.course.code, 'id_session': 0, 'gidReq': 0, 'gradebook': 0}|url_encode() %} + {% endif %} + {% endif %} + {% if url_eval_params is not null %} + + {{ 'gradebook.png'|img(22, 'MakeQualifiable'|get_lang) }} + + {% endif %} + {% endfor %} + {% endif %} + + + {{ 'edit.png'|img(22, 'Edit'|get_lang) }} + + + {{ 'delete.png'|img(22, 'Delete'|get_lang) }} + +
    + + + + + + + + + + {% for tool in tools %} + {{ table_tool.row_tool(tool, false, categories, _p) }} + {% for child_tool in tool.getChildren %} + {{ table_tool.row_tool(child_tool, true, categories, _p) }} + {% endfor %} + {% endfor %} + +
    {{ 'Name'|get_lang }}{{ 'LaunchUrl'|get_plugin_lang('ImsLtiPlugin') }}{{ 'Course'|get_lang }}{{ 'Actions'|get_lang }}
    +
    +{% endautoescape %} diff --git a/plugin/ims_lti/view/start.tpl b/plugin/ims_lti/view/start.tpl new file mode 100644 index 000000000..0bcc1c5d1 --- /dev/null +++ b/plugin/ims_lti/view/start.tpl @@ -0,0 +1,6 @@ +{% if tool.description %} +

    {{ tool.description|e|nl2br }}

    +{% endif %} +
    + +
    diff --git a/plugin/index.html b/plugin/index.html new file mode 100644 index 000000000..8e464d8ce --- /dev/null +++ b/plugin/index.html @@ -0,0 +1,8 @@ + + + + + +
    + + \ No newline at end of file diff --git a/plugin/jcapture/JCaptureApplet.html b/plugin/jcapture/JCaptureApplet.html new file mode 100644 index 000000000..22cd306a1 --- /dev/null +++ b/plugin/jcapture/JCaptureApplet.html @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/plugin/jcapture/README.md b/plugin/jcapture/README.md new file mode 100644 index 000000000..a26f11a27 --- /dev/null +++ b/plugin/jcapture/README.md @@ -0,0 +1,12 @@ +JCapture +=== + +> DEPRECATED: This plugin is no longer maintained and will be removed in a future release. +> +> JCapture use the embed Java Applet element. This feature is no longer recommended. +> Though some browsers might still support it, it may have already been removed from the relevant web standards + + +The jCapture plugin integrates a set of JavaScript and Java Applet files that enable the recording of desktop screencasts directly into the Chamilo documents tool. + +By enabling the plugin, a new icon will appear in the action bar of the documents tool. \ No newline at end of file diff --git a/plugin/jcapture/action.php b/plugin/jcapture/action.php new file mode 100644 index 000000000..d4188cc78 --- /dev/null +++ b/plugin/jcapture/action.php @@ -0,0 +1,52 @@ + 'Pavel Vlasov', + 'email' => 'Pavel.Vlasov@nasdanika.com', + 'name' => 'JCapture', + 'desc' => 'Plugin for making screen captures.', + 'url' => 'http://www.nasdanika.com/wiki/doku.php?id=products:jcapture:start', + ]; + } + + /** + * Register the eventhandlers. + */ + public function register(&$controller) + { + $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'insert_button', []); + } + + /** + * Inserts the toolbar button. + */ + public function insert_button(&$event, $param) + { + $event->data[] = [ + 'type' => 'JCapture', + 'title' => 'Screen capture', + 'icon' => '../../plugins/jcapture/camera.png', + 'open' => '', + 'close' => '', + ]; + } +} diff --git a/plugin/jcapture/applet.php b/plugin/jcapture/applet.php new file mode 100644 index 000000000..e6a44b4d6 --- /dev/null +++ b/plugin/jcapture/applet.php @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + +Java 2 Standard Edition v 1.7 or above is required for this applet.
    + Download it from http://java.sun.com. +
    + + + + diff --git a/plugin/jcapture/camera.png b/plugin/jcapture/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..f7cbba0a8ca3b25828052df27899b798919eedee GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`*`6+rAr-fhQx*sq1YK_U|3u<4%)3LY?cTFJ`De2``cnZUk8YQY`01%Ff)@it^VV|>K7B7jw(-qEaO0^|FJ zDJT8|fn$iF5#vg82UDjK;|3%B0GW;yS7py#f(;W_RVF6zFfh0;^%i^|buIvCErX}4 KpUXO@geCxWnm3>T literal 0 HcmV?d00001 diff --git a/plugin/jcapture/jcapture.jnlp b/plugin/jcapture/jcapture.jnlp new file mode 100644 index 000000000..310d39268 --- /dev/null +++ b/plugin/jcapture/jcapture.jnlp @@ -0,0 +1,21 @@ + + + + jCapture Applet + Hammurapi Group + + + + + + + + + + + + diff --git a/plugin/jcapture/lib/apache-mime4j-0.6.jar b/plugin/jcapture/lib/apache-mime4j-0.6.jar new file mode 100644 index 0000000000000000000000000000000000000000..0e65158e02c2962a9e114d9bd4fb510b8375d719 GIT binary patch literal 371003 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≠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

    aXxRHhIv;~+Ehew^dm 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�=;^v+ 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%0A((#4=Wm4vrbmzZwBcVb_pWJUxT5(g$Xg~*bb7B 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 z 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

    Q&`j;`7TY^MG)wdJaXR8dpdAXI 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

    ZIV_Zz7<_5G_qsJ2ny&Se~{BmPt>zebfI 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

    pv;zvmGnv&C=Z8Lr&;8^k!7^?BP*+H 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 bmiI5TR53vifD%u-O0wr$(CZQHi(E3mns;5dx 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 B18@KU literal 0 HcmV?d00001 diff --git a/plugin/jcapture/lib/httpmime-4.0.1.jar b/plugin/jcapture/lib/httpmime-4.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..47f15d967839abcfe2fb0e0c0723cd128bc3f150 GIT binary patch literal 31605 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 he~ literal 0 HcmV?d00001 diff --git a/plugin/jcapture/lib/jcapture.jar b/plugin/jcapture/lib/jcapture.jar new file mode 100644 index 0000000000000000000000000000000000000000..663c474aff69971b4aa600f6ba2fdb27fbf8eaa8 GIT binary patch literal 167769 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

    vVP)$9<`!B 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 literal 0 HcmV?d00001 diff --git a/plugin/jcapture/lib/transform-3.0.2.jar b/plugin/jcapture/lib/transform-3.0.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..527318393d27bdf9cdb4422abc4c51a29d402943 GIT binary patch literal 562414 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ΠzgHi)ZH{%w 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 EU%RY+k8S6Jh|sA8kmhSf+~~Lc<}qFZtmZT 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

    t72nH=|adVX`Trr=XnD;^s=p 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

    n^Y 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

    vEhApgXGH4i&14utz{h84>6oLqV|z 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 z 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

    txkB9|-f~^Ep z@q_vN$CF2T%%aa@H2{6b$WDj1Q;NQ;yX>`thW19vOOfVfJ)IeMlSDD{BWDpoFPFE@ z^CIMwej&HSp8UpX$;Md;2Z`90aO1mtu1%j+Q$V)|P5ENUTjZvwZWsR5If8K6je@Iw zNIRr!nWueBJ0fD)#~u0;5JGvDo5EFp#<{+6=@zx>R?W1Er*c@koVVhWCBv!VGH=RR zfCx9_Sx-DlArt21L5LLg{9C7&$cqQK^@bR4Ve7`(-e)H{^md*%Kn#6Xchw-cqxc=19~^E%*GA?R<*Ud&*~a*!s1;J>lS8Z` zi>f=WT+TUCxr^ouoqp+rR1Tnay7-YkTv;fG^%$0&xJyUQdvBdpbgFK_PhKW4zYIL7 z1evCQ*N=YAxX16mQTh?-5sf`aw3Bwi)_BDGDt!#qVh(f^j;kEThWN^|0<}%>Kmf+83(wV{iN}(ao_l4e`>Sr(v)n=Re`8^{FWOV-q88wdslwEH*2 zO-n871(L}UWCH|~r}Zr1Bupf%Q91Br9-|+Lx=A&`Z^$%&-@`J5_JF+B*n!{|oXr!? z%nvv7(#%*%4=G+xzt$73a_J5e_$z>3!B#)YBg4Fx5J|z;i24@6V=2e3!XxFZ*K@V# zJG|@M`=8TJFYxw3%jEi<=jpzXg~h{Jaco}fx^ z%o?*16R$xnAu&Jn#VDR_Cdn?rcZ{;PtAqyZ-hF44cJs)Yjb{oz25LWIcO=%ahh4lJ zHB&d{sPCr2`2C&l0+S5Rkl>FG0oS=qe&9pZxt>5nz?fob0s~ZdXWV{47{4@jURg-5 zoL+aGjfe)~!k`iO9q`pTSK^{TM%*TyrGKaWqDW(nSfIGB&g@jFxvA!LcRhOEa!^cOI(s98pv&EaK%jNpfAbRE4Kn0X2R0a%NYaF_ zH$7*t>~OG{9`o!}`o5lIWB}>-;R}FJpht?%`Xyv@9i=hNMAC5L3$RQJ=k8jep{6R^ z(fgNjFosjjNP8YfK}{Qq_Eq}Bf}F1%fTlo*y^jn>N6A8kR{plCwNlmEaw&1L9J-BL zqd>Z3v0eaB?^@oZqTC`TKLr0iC^g45q$zSANLxIi^FS#$%CwO=i#~h0knPcAr2z>M zbv#n^=$nT{Z?VEPUzxia7hIz%Qq@|J%pmL1D{RQduN`kA8#qHZqsFRsdQvS+m>}H{ z!+cWdVmDu`Z0%C#YXIHu_A>c~k<-7^Mv4wbG6YBDbNytcO9%+{N(=Js6fP4>d$R*KKkB<_(m_agzB zvsM3+qg2VGaa(3(b_5JU9mW{HK8Dqnw!OIHrH(y8xM@nP>Xb1-leY`e2JwOkwo9Ls zHKnZ`Q(9kFM=2*SKsm;)!#5Z#r8#P%NzEv~bN{fD^#N$A z1NPQWEO#II5}otb&Y{c=HAQ*y@54zPs+sRBZ|UiXpIEwSvaMD^9-+qU_#eX>V9(uO#m$Wp9`jb< z8^2G;2UQ(L`POrtiti|^G;saU)(`z-^!df?>lywcVFrp@Asnf2*rMHfwsNvz8ZYG18ODb6QK)5}HJZ@U;!7YsDqKr=LG zRVD}7nyOOG4-M|T&Xp+3E}+I_izQ&qqTblw*LibR##ld?p>>N@ESJ(b7>_2*ywcej zE7>fn9Mk#LXf5nzGGH-InCZq!lXvMg5#&5Z`A_tJw5l*;yMR0|($(Z%t8I7GY|&R* z_;1$z9ds<`(|Nk|GoY+Vyg=h1q-#d72-Q{1W8BgnC#2~md7>F*m10>Yx72wfB=82g z9`$enQv0YTQM7wSz8zWvCuZ-6=h*(dk5C!}4Cw>~-Kd@L_YK!Pe)X;9*a-tQkc4B; zMXHo7+bS1I9ubtB(!GLF+=9gg$BaIZD$sE84JcV17_Ig$2uB%p2AhVak9sD3SQ@`+ z6})iegSmDGx#VL7-~HkaRhE155OFI-)z(Qu0?e2N2yY4Vg&`olkx8P0lX8!uz?0DS ze@5_6`rd$*^oZ+*#_impj0C0S=*ALgEg)+@!uKcVLnc<}kPPfZP>JyM^d zdZBfe?^0F;;`wG6`5N{h3GK7*H*Od$UGwBU=kncQ)cX?G$~=9?>|@M4r*{g~JRCt* z`HXe%o}o8yX@=fsml|$qcPpdUT~qICbEtoPUl;a-4@(n`(LAuFd-#v-rnu4{7&_c* zN>6Php5u?d)2C>c)ZQ?G-$<>AS%C4xBe_FkpukH?2%@<`jZjL&f9Z>UX$cJ3Q%O*K z{Zd5vTWQ31$M7FMQB1T;c5dK6K>q)20O|h(+W$*t^1qpgu!%#Nnna~s*d32EZBRy=%q6K6>dqYR6^IX}j!7W`>~(i(dNSJN<>288zZX0j z@W_kDxade_5Df5T#hme{83>_H`r6IT^L|1Z3sMoqBtQV*RT*n&__(q-)F(Wdm10Wq z)ET95m8Xe+GsVV*8T;rI8}9z7(^X0OzN;9*X}A2L zcIqFIbHr9S-nLk{JLiNeL2%})vFL;B4=3h=?U-l9!m^;uQ7tjdmHi@jh8M&|U;YHO z{OVsn1yXJ)Z;!%Pb)6%_zmoNZ|M<^1SoPKI0~%l;p!0w4|5;zb{(mR_`Ol5^KN)&+ z8`J+a9a$>cN(=Hx0P_X%KtCiTZ~F0zP_IzjDM#fZpFn@`zrl>fM>{@45HI8 zt&FqNz>&2{Fu+O4SV_o`8wYPwNZLhcVb$Vh!_fkC!cLLnFlwpu3P7r$)lQHyb*Jo7 z#ZO?8+ifl0;x%iXmz-HU8g=bxn=WyREz~r0R8!Jq*4z5C^-m7)UX!F9!myYvjYKAD zAEyvSvI64yW)awy7B4%~owFos)tq2LhO@7=L}(9AbmJqrdiIp)X<*!_frmPa?zHC@cC%Ve=v7OW;O zt}UL>n#A&-rjcRabw`a3@4u?Jqzy<+-?iaHQy&eYl;`O+!z$#gliiA}@-7b_6Z?*f zGXI0FW`1AQzD|kjg9xds2pTwC1<WFn+pl+q(mZ%z1bomcBW>r!Ez1i%%Fd_y$Z9gpf;on6QUl| zl;W^L`WTd!z|sS04>si5Z#l-7GtzH6j&<+?>EE z9by;$w}jm-*3_EyHMS#GeGpaLg*t*MMRbT6vvjp(iOMANR}DG_Kd;jfJ~HG z4gXZ0C~J+-P#+^(4l~hGswvO?EyCywUr`>k7aEf3tlovMb5jy&3GE1L8N9dNGp1Dg zqsL4M{V%}cnmIEjT~<`CI7)fi(W=_BTK^ zA_`2%l(F5S61V;6RG(%_j4Y&(tas!2I(Dl*0byYDR}KcTZ@6WYdvF`Op%IzoI7$Gw z^Twh2jKgK7KMW50%j3{nY2jR~>w+-`4=$Pock`4C@`Rny%(bw4$R~)P$YlG!fZo7l zY?=W~_WFBjWBK)3C67r*Iv%!iKQ}UH+ZS#U7xtSub`IuvStVH?7N->_eKUz$Qezov zQ!E%!2*l3Pa2^dtMZv_R-DXoVIt5XSaL)uDHfJU`#>qaFqT$R=%llesBU9sZF)(*{ z3Z`C0Ejf}i2N$OqT$lbaEjF3#3G^7fYCmanwZBFB=$3FLg)PGEhQ?1411$Z4)^10O zO5_+swbgiBmFf+KV-`UK62C^ z;Axd{U#k7f1NW7r-w=)Ia_^EaEi%y2TC@jX>Wo2TjnvN+sA;Ie;&!3=r=3i)h})BI z(>SJ1d|9mC06-0`8*yqc6?{ynE}9aJ0NnAxx^aoBwKa<53flUo*xr^ma|IEWMJ)>6 zX~yHR^;?z2;APj813<#|w7QrUg`6Ix+Y}HN+CH$B|MJWzVK`TfPDB@8R5#d+4X2kE zefu`9|H71LD{30wTyW-5w^2?0y%T+@F_SJY2oaMVLD}+e3!Wqg*uhw%Wz<=(6uHHe za}xjY==N}(tsr2`EZONhcyt`UjXPz1x5&5kpfuwZp~*54bu=sma*5&da;rQ9JI`ET zwGe^ijiw--#m*#SK>kEm$2IG2hS%-%3r^d!emxM(t$%&PznCIBY~6C+?a zRHT+YQP#NLgqp{aT$K*m)|%yB#(732MpRa`tx|x1Dqe~R5n~MH*VE$JUlx`Dt#Ow4 z;^3S^C4<69X?R3AhrdkmkRztSb?l5j9!|XiEQ}ni+>BgYynIv6Mxp|=9+r$=v~iL~ zANmbN&KMZM^AQ*Y;djF0=~~)4wU?!Bv4fINZrmp1rZPNz#;yv}!>HJzF;OG)6Im(o z$BN_`vg%pt8`8$~MOR1DOI9RnX=>~#mD5m!a^&&q6W4~y7Mc@hG|Rso$ZSA_C*Jy& z9|Cr1fFA{O6rgou=PT4BBd{Zl7t~pv7X#Z!WLHYV;`rm*4c63K!N&DSQIJgv)wsjv zWOakuD#C`W@Jt7*1{E`LCsvsTBNTd@ZyknFY-#X7D2N=Fw18s;{@R-o8c`Pqu+@Sf z37r|OPv5fqLdI-2(u|nZsHdX=XMqi6LjaF`EQcQ&yq{Nx3?*Ec!>3JsU@;`$raDPX zTdQosvd4l-jYv^tSu?N072k+RzCt;64eX1fs&1-}|BUR&qt#^hO-k4US*R>*vMRDm zw_0ZxPe}L!6B93-02}L>FEAjhmRi;i<^>%ZX35D2&u_r%SF59rHW`pIMPm|cQJOs0 z2RQ+Xwa?&6KDDTLQWiAvhgK>^3HnZ&u1F(ZLLm5`a8*3uk|zEu7F2|J<=@;I$4*2X z!i*+~IG`V%le`(m^83U9z6d-K(Z>*le2i9nkQ{6M>JVtM+?aWjnsre+$du2S$_?t0 z1hE35LD*&q6vjQv=DgoH)KqSHk+*+&UmZ>%g#wBPgC$#6X(s*@K zX;M#&+<*Oee8_C`4Ay=0;?H5JWdxd*?5$zraf|4u7ypWlS%~y=!Wu~i{ovy@M(70} zF^y3~-F+L&{YteDMpytol~|1YVy14BI>KpUe5S__G@xnX+OBn>e9L3T-1Zn-p^*wE z!L|_BVP)0+AQy3};nZ zrycE3eg_HN^X707wGEn{cY^X#5cB9#{dS3eh``Dbx%Yx z5*-M#ahvtzJq5f3ZAT^jsy#S7=n2Hnmq|p6ud9~T$-rF0RF${r@-Zr@CPB|haSL9q zN?6OA;7W5=nS0hKa+!ic8yjaUXcSk@VfXj=&Cpd1S)=OB*JF~n^eVD^Zk*IzVk-THVpiLO558(iLs&)){a!F}W5LO&?6{0#cc7H%PV$0)S@wZ0>CRaHRP<%iW1 zTK~cKPg0}s4xCj)m=EG3jQ!43zbo<%VmYd14;&9jr0W{ zjPl>qnUvHoNP8D1&^)ynVWa^anhD3*OR76l=n13G4;^1E^l4#cS1^_7rP|Asjbox_ z!nxo4i`WCK z7oY<{3PkGH1Wo}u>#0`LBb!y@iN8lZx_5DlB3%hVzw7JLsOLdM@N9fDfS7T@(QTELAn`%Mz|ATtpvG4mi^3zBw7JD)$gR)kDP{8SVKWd$FZN2@)sl=B0u1ddIh(HDAZ*}`zlAXk1mx0T)n)VUWX zLX`#}wgw`6!y_jMOH=iv6BV{uY!YoLyN}ho^URZ_whs^Vp>YWud)POnd`)C+%Mp<> z{=(q_&4(bK;OgcFTE`pstLd&hqOlHAg27A}RhtmcB^>98*}){?mF2Q#(Is!~;2o9a zuh09xHP<26c(lSWmboy^nA!Z2PnBWot3KT02(uES2bD>*%cADIs_G$n2H=1{VwKNf z{*A&nfXGkIAlk5AUvvVFc?#jV9B>Z9e678@kp;FC3O-y+4i<2HU%^}TznU|Jt#Qf) ze>snKXqk~-kg`}$2>NOMDAN%GM|E^Y6I(?vd{f0-NUR>Xd)9eN)$)cNuWeT>Qfhua z2V;~~Tqr9QoNA=(IeHXcJEk9I?ENU!92{Sh=!?aP0+h$!Z!K@IoOjoTWc~higAg+h zR`Ri_0~CQT^s-rqNAG;26(wWjkj{;J#O-p=Ui`2>zx$DXzl7ys0;uU`u&)+vyTvb)IE1sIVEi5&u z)*QRr(%{CFSB^$9J-Z!yZoE#uvTlxtB64^j^?(6K{rJjzgWT!AdT(+iG2zOQl$bDU zt5qW&NCL5N(nACBp&s~WmySzA?|Ukmr|mS0lVG7uS;7r$dF0&H>8{h;0@g{iThZ^4 zrKgK#r_&n&(oMC|rL@gHx-OcK9ysSQ*R&?E!ebTyyP_B*L3eM3UaQbcj#-_DYqV~n zkIZNu!P}DVRKVzw;+GM5at#KyLB4AO34Wwbtk;pSUaDOiD}i5cS`yH%)vA-KBd3pf zm79eS-dY<+wJ4*2*@>0_ST}B!ug#|Q;21J?tJ3T8uR^0Gj{E?-L|M1Vd)g4K@+H4Q zHsMHhijesCqYR0bjK-Bh9_jXlLT~adZiiI>zzf`n_fGrojv%lQxK_MJjV zX^+{$5WGhp!9Q2;@KH3HEmf8}N@BSlg3sk@jU334wm2Al8T%e?@ z2-+uUrITD3A7-Nqq|(H70dobf_|<1->MTV<5bdHPHfmXEtJP4&Hrr{UDRmM2Ud)}H zhJKCU5^Q03cdh4No0~_sti6Q{?ea`+FemR!LJ;}vayxO}EzGG{b^K=qRtwcSqKRT{ z-6doLRU6Dxm2z`!NNaOrAZ+_szLRT{F#Hw+aHnSaA42DCbb|)ly)^t~9}P~)QMqxP zXq6Q)&vz7B8_LxwuO}CG9v!QojfO#Wp{wKr^kSG->62>#d$Vk zAB$**z-6xZGufUQXI-RoX=+25US)XGLvtJao>{4cy%oGoOrY>QR?P-lWQeg9a+I)Y zYD@Zy@3V+^#|PUOP+b!NB^ouX zlJt&hip4P#*yqQ=^NpqBKF%Je#;7yH8-!uk;C-vC?OQs7LYHWW5IZ@WWxS&&6S>9& zcoZAd<~niO?e-F}%1r{v!2K<&Pno>eXEj^Sx)6E!IASqj6#$m$A5lgEh7&PTG`bW{ zI@Q8y9Y}l2c8!2VeqXq_ z7wbAc1^O12#L_lW)75jWY%OWI2eYqC>F5S3n@T1*f{6pX;fn=SPyM0HA2|!t5;u16 zj!jHzkvA+CT2L< zU)elG90?~;+gbYb8ZIu1x`7-8&a}Jn&7mq&+H{gV>$Pa3nThug!bBX|gU}qoc7stA zw^V!>q=_seUssoYQ{1uSvK&L+TtlXt!jU#(!DRw6Z1K zZ92nMHw$4?1&nRlBggR`Ycp&{MmF6NfoJa9B@&=h9>6}ae%Q!$G|*~2_O@3yEvwt! z*V`V{ao=k*2>J-Ou!ci3Y>-U?D)F|+S99)QLXJ1tO+*Rp4>VuV_wMbZpDv%L^^A-= z8|3hEP*NGK_nXp(E zd2dF=ND34m+2Em&u1zbyT<$&$M)8N?@96yw*25GTFZU&{+-bP9~-K4s$Y_UGd&ru~p6A*a*evfg^+41tB0 znDj!4;W>ro@DAs@DxR5T0?u((LQZNm$C1IT!FOFqaPsG39}(IrkD!7T*_bfEH>N&5 zu|Fm6(8%ie6TDf*e0Z&VtCE53fH@w^BbT?x3R9_6tKOHqwC`K=h2V@qF(Kn@+WIT# zMR??FVD~|cxsk!$898_c{?sSnk7xgelA3f@knj-)uyH{xCdd&y@mc{OD=Ai7o_H;y z+IGt*Fe#q!>bD=(hCjSC_Hy=im@L0dt4{2nI{i;>-A8Y!2($;B;v>p^!45fO1wAFF zyfw@|!5rB}2U#N95?JnNWy`s|-qp0$^D5egzD@Id!MVO!7(ss3W(CqGj_a=)r&q6f z=-)1F4RDnpA$xz{4;qYueF(jY3ZI9;YlV=$_N8x`2S>vd_cqY-mnq-gjmz_(&jl z3G`yRb8~RO4XtaYQt`mcxtC9;!+(z>OPBF-5ScuvdhnJos!ZyhrIj;MbZoLl*Ft6o zEpS>)QF8}**-tgtVS}bJ$c+FUk1iM@MmWrw3VtB1);iOQe0Nd4xG%1@wVk1oouhE`IGW3zhDXm-WFFC`diL})n8 zYE8$vMX>0X)QI6*_uE*;yLr2Q(8rVaK8o`y{4Nl4m@n)#?Ro zN2*FYsE4B_Yb3{bTg*8Y!!sUX&cy zVJ6^7(YHcVWf?d-M>$;4b!c>+q-&IVCN&$_Bv_DC9xjnANFWM#NLzZsqrsCRFj zAH&d(&Lq&#*G?7CQzYZ7{7L|Jh6gjsu>;x+gc3Kld z=2HQfja09}?CAUpFt_pS@IA}L2P#!azDwa_vtEfsN%Gl$Bvmx9wJWA_FB;Pt1!+pS ztud_UOcts%z^_)0XF=0*2unaA9_Y5>Y^XJKy%vXGDW0Ef1P!u>oT(wq4!kyx1Bbn++-mtn9c+DJz=YmA(&t8t zIqn2rfK=;U%DkG~xa{&&ymC;x_b!Eu4Ps&y%lF~Z%6j;Caj3+x*HOlMi2+>D=Vegl zol)l3Q0D0o%;gj;dyOjll)RR$9#!qHsaRFcR(cv{Zs8f1xon+3)?C+`FKB`rjhK4E8ALfM5%LhxQS{MoF!XLR=_wKaztgXHI6BoE@#C+(135+`5D zz9cn*jc^Ozg9l|#l+wWVKOIa&7E z*SF!pG0F8=&w`$H19&mavb63 z%Q9|`elLW^L&)y@&P?zpQ1J!6l2#(=T4DP-h;}Uz8N6MYAx%1Wug#P*l0UsEfWRvs zM4X9=AzjhGDPQ1=AF#_oeM>@Z{}g4^9 zKc1k=$n^R2aAlXUc80HN5^7dmlMSibFdFK#GnQVk0t)JNvcixuAS&U82d@%`hDmK} zKE|SmRjuj#A7~RDorcv!_8GEHa;+hbTE*Jn_kG{R?}Jl|ZO_qTk8(G$hFf)f6FAN9 z)xwQ)B2_0Qf-aAGJzU7)VT>XcJE}??hz*2>n6cViY=o-kGSPs8QdB!PAG5W;rwy)_ z2)!N}NZL}g@RA1HQ`Rf*S#0MOJZ!!BpKwQ4M?Y--j;ux!5yzsvk(R0E54Njlb$F3o zUN^b+Y`u#qCU5eJUtLpU4mTfucgHWZt_h5Z05{hGu@6{Ti$!4&6{m#U@f4UbM#d?L%vE%Ug@t(r>aE*X)LdYA-U@ zLxaj&XpTgg-cO?BuJ(o;*fhs9@$Bk%cWCE6{M9)EYBAhY;@qH2_z+*ORH%uep<#A6 zey^52sQTuKmP+x6Jb9~3^H{QwYFdW9f=Z#^z%p==;&FUR1V?pDo3T`aSsT@JBB-h z=>)br`Q4tqQ4<9hg@^Ui+YZx4bN4VCN#ASh4Y@W9ovD4oxbaF=9$B+zd2pd_=nZNW zWtTtn%GQjvBRKks*B*IeRB{)v7-fy+oGy7Z>B4476X_ylfv3pXn{E`+-fO-rPIm&v zHq1gNEf&3!G=eHAJpq?sTExfarv%1rpF@7}KmmWcZ7duD?QUoT zy$4edE`C7j28o0d3?z}XJI$4wNxTNU8Tg46BDThg5M~;`=xwsN^}@)^iJeKI(LPr7 zEEaS!bq-K(8V&j%LFKob+6y9-n&WFfUmi~o$>vbR)`f8BNh@NJe)A402%Qp#K?hWO zN$mW`rvsEeVN(9-(rf>o|9`MUB@;6XC+GhSH&p>-osh*5e2B+%Et=Ty`-1-nAu$j@ z)_wv*iu4ca0}t!{!7TwPx${&zyD9-_2^@7w8{B_Pm$xedt6;!OIU06oTr^oWP0dX#Hm5=SBleu zSY|&!WzYLV*$X`ZQdZGdKA+asoo=_R3TP!!+{6L(TpDt|Kf45~JjGY`=LEyE= z-m2|JULo<8IdvX-Ue~ucVkY2p1y!-hAp}ST@5T1McIn_gC(aBQY7*?hVNyV)MhG%D zF0ry#pR(-_ylc{4j+lX0tG9+zLaS&u*yFPQ5^Jx@4zk!p;b>N=w3tzjek2nv(NOjU zd=05Sjlz0EkJxMP?k*b{-0&(@up1u$Ul{2R_tPBHOd9p|({461ga+s+tdGWiH4d2? z9Ocy$Kle4WkKeCkH0rwdOlVdgWhM8^B?v6TeM+?Z8i_IP3qa)d>jWE^L1$hzZa^+R zF3l)o%UkgDWCZ`5UXKVriah$dUw}Wz)dnuv`s-e5a^w<5H~I?fAFn{I%Xt* z6lBhRa9nyQw+EEL-_rX77sk$A2~7}c7RMH%)_w<4Wb29(h2iH6XXhxV-))oBxBeZ_ zhHdtC_{B+}>*HS_GQKPAy(E_Sh`313y3Z94$}aTNC*$TPIH{=VEfJy`j{@$!`o-TCo)a87t3Q1oYx z;rd5%+5IGDEy(lhT>|O$scR33A?HQ9dI6Tyo0wrI>%-F$6(7RA9jb7rOG!u{Y z?gV(Z;ckT>U|$`9`Ic3~Z?JbIAT>aI;uh9hxv+f-_Q~ z1{4r+wj$*%%%Jt)X=}SmY8%%Vtu+}Y)1yflKQ*=$Z0Xcw3)p8;BkqQ_#X0L?rUgw? z+#(3vv!xr_F7mVnr7RB(zaG|9IjYJ`087?539E9YKcGRO{=}wCW?ZvoH;QJXV0R;4 zR$;=~pv@}xMSy9=5D%d%30C8}d1SKaa82V;gSX5#XtmG_PQA>TX35Hx+{ghl!)uan zt*MaTf^`GDCK|=04nrfo`&mM|tc-Fk>%1g2X&kBZ@DPf8s9U{rC|n@MLY&%ulyDqr zjIK^56JQ=aNUp*S9*@@GwN)>P`5~$%V}&9Hb|7J-hVDbg>K=6uSBNwTR0S|_d>OeK z$FXgmoc?f4^O56`N3nJEcASPBP;Ctnj4~XDJ3gG&CO?rUYfqRQ9&2$7XyfFLm3-VV zKUUf*ixgYP1A(Y-6JaWWG)QPL5qEYV9IiC7G1OqPj}BYUYGmVDNxNU znWQv%FY34?_z;WPZ)y#>XNEyUCYp=aFJ=w3$F_;r?`h3o&lhO0X{%2Xh;C09h<;?i z5}0Eo{JSSMDJ5_{su`hu7FUSg9Z40X?g(qx;GDsF3VlQi#WdXS`Ky*Pg&6`{y>Dns zFW4S`euzSg+7R7iTc|ZqFX-0F;RFG0;=nH4DPPsV!XHAI?W?LkA4ExBUC&g9ilYR! z6RH2^*7A4Co#yYJls$NN9h`S!X~R5Ekxy7eKjFb0fI3H0VJ|`RcbH24s0dmezr4T8 z_ocO*Mwe+$CPCaAGXkx7?mR*}N@Ve_j+4CF}o8SiiqOTnuWY=<4^ zQE`y1Q1$a&Numd-6HB`X)t7`@=<>NAK6Wbrg_708OXD@FR?4;8Z~fY-_P%MU66`wW zT$cJQ{)}$cdN(^@a(nHcI%{}-_ls8z_*Z*mzQ`DXyli>A$^$bV?BDa`g(0)d9Z=In zhX#864%9h@nW$!d&cDtp(YA&ebJ14}xJP2A{@|{TX%I$pKHF!b>G%TU1x4Iu zhtSuMmhNnaIkK3`I$7I_Cn&hhlQ8B2ci?2sO ztCO~^8#<&W?9sne(=7{6@a8+pR4ddX&eC&k>gNbwXZkpT!cox``b%+|mUG0Cy0kr^ z7HVjnmTq)ro_aZd$s2&LOt5%m-LRBjJ;hdu)l4|{YMn%=R+VO79eC|9jL+Ch*SH#> zPJzs`^q@P%^??}IA-ymopbfAKu*1!*4y=XIxc z^*N(B4wY$r^ek(8BF*pSnLGpG+ZD8GC6St|1!0ZaEru?gkMP(mSPw}U{FEHtwK#re zUX3sdn{;x=FuVIbeRt)dM2z*oH(Zufe|1PjxZ-U{<3IpM*Dd_7;JSsjD(+A%bCBZ* zFU_()+Fp=PbCZE+-9r&MfC9YeU7lk(CIfG5Zn;Yq1Ps4bYExw73N+cC#OgQhdF4mIj1g&UzY!_DeE$F)-YD>=IapCkjrsQYQspDc{6j1^X z7nCJ}szShV>AP^~R~gi=YhIIv^qQK7O6|JDFB0!y$*r~K*d1xFwAxzY<3iZnoUW%i zpkCvze>OFjT3JT@pE4>J=WEEU{fu}gp-~g7%iWS(M5H;~+kIO>Tw@6r)&9!;bKwOX z5mHz9gw>Ro*qkQ+km-RDr%Kkt7->>fQfGwnFBcE}to8>LV|(Ftt)E((m6{yTFs?#; zLcgp#g}#kgIbGv!_QAqslw4jF)>>I-{RYzpnuPNc1=TsEb;_?ESV3c9_n_+u(UI7{ zH6Fk=6kp(7n}|Q60u)oOlnEf@sTp^WlszaJbpBLUX+5$2{%58;NB^(x zQvc0fOiDrg|7NF4Qq%d*Ud;A*dUK2cQc~hitPC^?Atp!x0f9n5APGaN00}UbLPo~q z)M4;gmVqP0ZySq<#^$B(LO)d+i7HmrQUO{?bc3QsSB)y^p?#%|_3G+!`Xq=PZ~M7@ zx9Lq8GDgMsSf}YNvm2l1x9)$t);izD-VlK%7)}Qaolo*HyjTXk?ej)P;3q;jkoYb| z=Oc9StqWN$9}Iio`EU^K`CuV}=56>6p(SxztXc;`h;ZiZOK#_?_UQ0U_iY(*(Bkbv z_d=mtpZzhmm+fQVft@4vbQ!&se>vu;Z_%s5|DLe>$DQ+>4hXxed-D+m`!e!S1&^@s zRPRB;%2VAgRn~Mh`Cc>+KhZ*kW3@l|cv2K=%_%fwu~eOEf_Y%?tJYL$J;U)PGoiG; zWFU4(`y;9Do-s|Dr^x256^OT`HFN~eXUI-8R-m?0QMpklun!V%rf=(O4krWTJL0V8 zHjIKf=(h7lTu&vHy}5;!u9>`01F^XFdGn@I@vfQ$ygPCCCmL(p8pIE&C%G384TC}aNP3Rt;&EN&sT9qoa zp0_*YMFN(u(Wa(I0|Xs%lS7p7s8I!4PM)s%XXEj?_Kb#ZH{bhn_I*kZVpB7gU>JJp)O;zVH< zU-F?e*r#vhAQe9!y-;>i){I8x?<`CH?ocw=ou>ne?h)^Y0}Gsl7Bu_j7ZMhnmxB+E zJpJrP;)H|TwBKPHD-X%-S!M8O7p@iTNfLtz&4_6mlx{^}KTHf@?A)lez-sP^n&h5I#fj?yY;0SjGs2!iLT(&B; zx9{mcC@7In zn^jj^);LgnUR_TciIF$VM?S(i^@pgFqbLu{H0fq?+hZuN4eYB32ap5f+c^Wx%qHlQ zX3@&}4Op7*NR#LLidT0%tYF9{m&n80iFihB$p97^5lfq+YDmYCgwo-^b4T*zPK2_!)j_pl#wpbAQqN1Z36+s;X?G?D z4b*2s50_i{FqOSHtProSEX#@gOXz5vPdUA4f*nq(fOg3n-{u;-8b}E3uSX2q9>)+n zFbI__SV3Scp{W}&rai_cgHTZiL3OG{A!N9D?Huc4!9g3Fd@_g|cQozTDz3nv0NzGe%e0l(*S*9*7Ygyz{hsN%tx|&&DM)%3w-3uqI(7~U4ahYzo5?gyK0m> zbhBU-JGiFFd>!GlMj>4d*BxsiByLBl>r9x!ope+*4>?>qmw$#y;A? zcV*2_o(#9zhp0OMSZxVG3ZhmTr&b9TTxzkAfJYbMkiiUS0vGheNietcQbBPI5=EZn zg)`hg{~meeJQ$GxhP~1ur5&s}pp{Ym9B**bDU}@kVP>zm4sc51FTie>wyWh!zy7)z zLnJz8leifT+8R*7?ZAHB|Am9U@AK~POMVXzXP}G2|Kpa&P3{0V^Tg;&h37B(Ed|{k zV`tRJ9btAa|83!6ZaN2fsw!--=aI(lbRwPkaJC^sjd5+J=Oq|NOwd_SSH&lkS_r=!a95R*pAXkXH3 zACe;!(3WY}zgJ!jh2ua;YUwBKxp!@cDZpa?`5x(EdIYo#HfWJ_09!6brVfaT=!1=9ejO~PA)RG-uE&?VREE2y-+Yd1m@?PX!!Qt~)P1ybM{BU;kx~Vx*snWro{haM{d0=`ASY{{gKyF#?^f)Cm4LOw zGWVL3faQiX-*`t=;&uXwMLKfmVhH-9mYmQTnLt-&@(uAHJyX>DaOuK7a(e2&B&YwI z-dY8@|J^f@F#b;kD@oZ#enA1n_gZzi3XS~^>=z)hJY$rK<9FDwaeWkxZGgH6GT)X< zO4Vvp@zt!~p&2XVFoy4+o?&=jw(N~PG-OEd zCS#qH``~aeN?WN`QUk@mdHc=bJi=qO+gx#5R`=||S1(bZDQ*L!FM)_)o6zu|xpo{r z+o)nXhEXgr7)T#3!H-58EyMlm7cD)Vf<2#H>?2`@cy1E2sl7wQ5(xSVCq)x{J;!XRK8N>-NZh_+No zc@h8bkMGmwnf`%O z`0d{=%4NjYaK>AP-L*TfHv;KhN;RG9`s!TDvF8xDBL}vfNKXdwOmh!aC04@g7)qQi z0ui3kXemdgheHz37MbG79OCB?@y5XP-G6ZX7WfG76#2$x6cVpq6tahyL>&!jRN~9( zWD)=)Ou|gK6Y!}5;3*8UJOQGcgx%CXg!?XuiiFjM5NHla=%rDB%}IF~#N>U7T_O{p zp|84Saa9-NtLVy!$7w;YvVExZA0R3gSvp85pCt9ZOG(ElDJ!L;`JVsm-yi)fbSVEt zTweXtQ~#f5A^*+({r~zG|Cg4k<>9@!)Wm<{W_HYuNjCNigMh?4x*g?YiqE`|I2EbxbXWPwrooyB**9;6#FiL(!ND-{XXA^>duo zI~IM-ZQNMNZ(VBHtg$Cu-7{kT8YYjX7^OEr+T!j3Yv=Mv$=w;5pLDDXAJ$mKFH4rT zF>xc~6ggdbE!L0LAYn2)ghG{cHjDoGOu|C!B8!wD_BVM?3<9c zKMT5)Q(UA@=CA4>;n^EV=P;I?lw+l|X6an~*H@-)?a-RoV=0U3cufApV@r$bxH_w% zHo4gguy(<8?4mZw^o*jmp~Edct|YHtZ7S&-g>5?Nyz`Ia)o!C$Zc+G?r5A)+XYNg{E3s;doU+hBfG`oB&0Vvwn5>5>twI?`9s($}B`rfHKVQL{$* zLkuNRC!;c_nH*7{r&MKJ2{_@HSA$NaNa~W+$ZC%sQd81twGM0C_`cb1S5fXA&z$Gr!(#y1EK*;|>gdKxGKf={$T@Zf4_Z>%v z^C~LntE=?Xv>LEtXfPtGE6amX7+b5WyS>5O8;7kYJNR&@p)+6BlmR*VJUDB!eJls5ZWTbh$D}Jm>FMmMtS;w9 z#CE$tM_L(qpA3dUV-Vo0p@D|XYRjsYYyh{$tv#Y|Amt2h1fO&hx`k9hdFd?j+=s>x zqN+N!1bqLqra(3UV$|g6dq=8hy1HM_7a5IKAzJz>d$0y^O(jNOnE&?Ko~)wZ3S=^- z@m!>WwTR59o8K2mgzM*(H8l8WIe3|2Be+ekBO%6gjP-NECr+R5q~dew9#qmr0;2Q*O`*BNBVUgfFN0MNIqbvWjcuy$*7A(l8A zxbDf=-NZ=?0hZGJ9(%41t4n`(4d;c|TOUy-T0z**;5Ba<)yR=ZqmjZ#6VNhdV}Mqh zh){G8x3T_aeONa(8$tgyYBZX#q`oJq*+GLQolRqtCpeD`W((nN?-&%Lbz3-5RMZe6 z4E$SM(MG9+l$?5w%rNbfPCH^QZpHHNGP~ADN1F;0> z;+MEs!NfR8Nl6tGj}X^E@Ukq zkN|=13)~wN-Q0@1+LJ?MOC+XTtfEB}7soQUn}@0!sDaZX5R<9PCSd#3Zg7>sHPkLd zt4gHm$;;SKAVUw$xu6^@C%4p*!d0LUOd|L-xq%hFcKw$HrbD8?mxK0!5y(Du!G*C#3L#aocKsjRIrUPL zzbXi0T=I-jaxzbluGAww%knd`US#}NCuXl2hsyl?)s1F1>oP#5LrH%zuR*3 zj@3RpW1){`Ylv2GAJUyZKhr0Da4Yn$=|4ZEJvZcg5{?ypiL0r7$SHzd#6&@k=FCHu z7itvfa4J$FdUqZcK>NP}%wJcu_e-b=;C+!1e#B;b=0|+rxxWVFb0i-0%wK80t8)0z zH#)_Sx5=lz=09PLKDpOVO1wW4gtVV#s{I8#0gSKjX$iK0npIG3G1; z^jKX**)P3mZwVq%BqFF0j348?m--%bA~<(*4kij4!Hnpb zQl$O_BH{)&ghWjbBcv9Ij-0`9R7}C$FTuqvdDGBCNxza{*@!c5 zPBwS#_qp?nQVKAiiZ++#m?tUHQ=Y)w(y8Op(etQEmm=Sk%_}XWwTq^UHI^tZa-TX{ zh`G}8uR&I$k=&Z2%YZrxEAD5@E0sN(axjIT4?O}+wms6Nkb{N;tBh5eZYrZ}SCs0F zitp3m^fXE744E9QuR%@xEM*E8wpH3p6 z9>($V+gk<4pw#e>hR$}o6V4zH0=2CG5`-h@Ffzs*fV$ns}v<)kE zVJYfZ6>Z*Hp*b=pN*-Lguu)lr;ou@|O`8?ia$RLz&%*6tdOrwy*1LkH4whSAil)68Ns6m$Al+sCZlg|@SJ+` zAimVM8^NO|&x99OnDx3hu}{~vGae;dJ0>q~7vI`r9qm0U*EbCxA6MVN9e%Udio)l3 zF45O78wWnFe!afmq1WVp(G#=H@j;t`co;qXZ1EkcJtsS2A|Y5y_=PTnwSEV0fXa()>uV2{z;et(-nx_Sg==3@FnQtr<@Jb^nqOWzI#Se6QNX60Fbz`F`L z|0OAzI(bnpJLK`x+3~~HfH)C{*W2bU2C&ov6Zi39J&Dl$nG@H)j^Q}XAy|c{2;v)D z)iX!Jou53?l;)bm73VKWIkI7i3V78WTIQ1~S;++yt;rC5#}B7Ja>4ICpg&4WQ-9*v z(~K*(o)bj*YcEv1%&%^SP#W=@Ot(t+T)A4wBD6gvi&)*Z!Q=$Ic6!CuV=)Qv!{yw) z%D8V@&t&oO8W`{Urjx)wFF5!lZiy#Uc%JnNraMk96rWpgrn*k=ZUx86gX$2>inrxz z^KM~HVDkur2U$jnvTopw;!j4)E{QJ)f+nQr$yy#Gzrk~qYYJJI{{V-5lLvl7PL=!= zyP^!?I;YwO)jrn>@*AOd_2a#>W<1I-w*5>EoKinOn9GSBrH`*=vgN8~BdJm6vODuI zaA2#xTPaeT&1clAN>^DJ`$|5@dQKGXprOfScxxj`*bCO;Ub#?6T^eUmjgNbEhG5;- z^F)DJEcO`H33zDOLQhx=Z}@6-!5o^j1;e#7lm^3<4^K{gYAsjUNG9Use1pAWjQ?LAxJWEcMk5za%Q>6SK6a4+}-RpMQ%4K<`}c z{wTQ3y5qQjlitx_4L48*ohV?<6ft`9m^cv1?!P6@kk3MP{*eM!vqw~U2m_3eD}tg6 zpOQM0rpz^I%dy`oo$r}AbLYsoJvg?=6C_v{(8}P-fzj>eULz`3U zNtoRaIrHfWsXe@$k!>f%PTMtunQ=C`sfK@&M$PRzjiq!VZ0xu&pzam@B}Y{1_&wtU zot1oMf&Mc+#k4WlMc(W8_qq~~OLAU#_Q-s~!+~*=1j0UQ89?}FQP)3#GkDP!J6@pXtd@dFiflzK5%}gcQ3@u7 znCvc89*2?DP;qRut9-Ps{=x=3A(d--*lbUkvbz@WH{XP~3#CpR>JqU}^`bb%lCc@a zDAr>(X-$|s{1>Sc$9`gv1@!EfBX(<-(wHS7ES(J1SV;x3GlW2SrAl z_ZtxEEiqseqAV1@!0{U~YcfU|mS3>x6D5Cbp)orr4-SQT<88zlk6%9J7TOtg*ls^( z-qsiMY?%uGM2MtBF)X=TqJ1UKxrX>mS8E=8{N9hN2`T;`{F#vKfI;Z?eR9YOKK9kK z?>NypFoYaaD!vWpUUWdenyuoO9R?=1>|VcTI}Cbq}l&iF|j@Z%*<KzH2BE zoDmNLAH^VpW#w)WA1C4jvzoM5LPQ*tj9CVV>fXkXtB8tg9R59jHcVm3GOW-LCx;yg zbI!tKQ4uGKj(KnfZbbBifqV)~oPozkwy-b^@EjY>LxB36j>E%Gs>fhg1a7$iYC zDD*Le^9l15$!F{Qb@U<3kV^CpK`lU5C5Cx3-%*vPw=abveC5PNMG8-V&$^HIOqOe~ zC_~@pW601A`$*&kOltLUnV0B$=+p9CH{*PyJpNTXN@6CmNq6lr@DUfM-19v8PAr9) z;ei1+FK%AO^9i-(jq31iYzFxT%l0 zsn58X``gwYX)_nYRso8gA^ai}*a}QfQ#yN2LfV|H{xvW2W=`({C`<2+dQ|4#4_7xnyE;6|% z?lkuu=$c^jbe&;*Mnw1VA7FgO-~{x@mWVUHZbkR)9AbPvMfd6bd#wTq?#KJqQ`c95 zv!)RCm-yh=7m#CL`BjP%U3UUg=ZsHxyu9^>`&;wvq64f3jx=EhbNpdPlCUC_oqxqNa(>}0$0ZG)#j}8h zG4Y>{uUNNRvw3~+V~rmi-Of>)v%J$BP?hiiZiC!CIHvckGmk@9Nj@>Kl;F%Hjd94p z4g{-6^aR!;eIUoA7+b}5BzsqX{gyK>nJ?wv0@D{S=;%_^txpoC#li_P(Q31CLpDiphDO|gDCSJznNd)9ZIx?IApe}DpE&97w+&3 z>6FBmdS0;PAlR8g5Sq!67AmNme}}@cs0q`9M-?ni5G-zi^KN|rInJ70s4=Vz2=gtv z)BcM=wjPor#cWN1zS7!?p~`8feiUkuL>#OVjiHJw34?6Scqjy8#5v%b=NaYeA?S>T zl3rIZ8${p!#0th!6KX0K)454PsMCLT5GYO1Ey9=&Qz9IgDQFh`aqJI0 z`0s-jB`hxd>svv!y;@x;@C2GkS`1SZIB2+z5kys1B_K@tgfN_J!%iu|16C}%PPmOX z(*0>{g!PP2Zo4_6gO*U<8v=(eX_a9wfR*#zhq9Ml}9iCx6lOdN{M_{#S(a{6iuih;*8D(30Hyiv68At4S@NjnzW#|h;$O< z-`Np*@fQ1#z8kyTnnxJ3+>jq*?@?d>`oRM&cTo&Fn)R~?XW5m@)Ke%rSs)fA1qY*Y zV=dlwq%H+;NiN#%%U5nrNP5n_4VDAh)}pY9#VS7E>!oG6-#HzKx1F^PEObkkKz-39 z&K7$F3)`oQ=Z-87YP}%1PGV82Duz4LvcnkV_6)$G#`+N@QCM{af0wpAfB>`=VsXWM;A8#@;8m`z=!tAVrSs`6D1$= zQV5H9gCpMQ@r>Q8bMLt5nk5UF&AjoPiEJ}dR?llzb;U^w**z-_4PmeVVQ@g(^*B!< zjjXruN7Y6Pt@Gvr>4%METl7JNJ*j*u_F7fK4ST7A*&>T8Vz~nMx0cz0ml)1`j1deAheFCcg^u@2o za?d}$dC&73miUfB_&()uwbH!(z)uu0DiFBnf$f=g(#-qdeV7gbK*rd=V#2ub!d)ls z;uUtf3UF@_rWTJ4EuAC@AE>z?ec2uM2NBPNVNI(KT)B0v&~pQF z2qL=z8J?K~>vD)AyCNB$p`7ojaIRQ6*69(3eS;XDk(}?35rx$U9jZbPG!aLdh@!1* zoQ2dgko%WEcZ62&w9R`Ye0#h9PRIDYY@^*a^Bz|zi9(dM<;7^Cwa)7x04Q|26Sfo* zNpIF34(Qhwwm|ub=2k$SAf#0M3R#}`qEzq&Xz=F&PiEt4A*b$74*fQs-A<=(Jn}tM zl0&j*$~w1&a{wQ(*ob|O>>2Xi*|)x-mK9!-<_&&QQap>(u$ehFLV|$+~T`)Y5Sd=UYF_t zm+CLpwiL2EBHhTO;aPjS`IV$QmPq-<@^mN&4oRme?MYI$8s@o+9H-F9n>@jueup+> z-I|Vrp8magVmUm1=lY+}gb4)*b+-c6XV&j-LVm`!bZ}pNrnzrzjGs_?9=aWDz`5O9 z1IxDX(!3dJs!hbhU%Jhoql^-nC3S_IKkeWJ*L|fBr*dE??RvVKJe563sjnQ1%ABX- z_#UAA@3Yk>SdwqmbAZ$FTqZR#mA3j*mKBcmQ!M!n9pqEP$Qwu5{0SZ9Q_~f|xr1zh zm$ru-T}3{UZhn>(UsbaoP+jbK)(!Yg!;z|lmPgrxawz2C*v-iG^9IYCUPgn&U|BkNThKC(NR%lKd}XCYZ`NaVI(W=eDJ6n^cF~@O zJ`x@nlqJ@rfQD><+9_yuFLdxj7o3h1 zmQN(z(z=;XFZD;^+)(`j)LNOQt0omU>4Y3#*MfRi0z7jxJcstd0h?gdwsU|gWEnJz z3Mh;Fwb)Ic6LQxIDGL*a3vExXY@jyC6(-@aO zM3XlVm=0c+pc+9mpE&O$;@0q3W5~%o?r&5_FUD76?UW;0bmfxG5|Hhm|HK528leEg z|KgCI`Ti?RfbRco*`Linho}j?n4^Ks|8iRusan}%|8rYa(^%M8;Bs3MZPW?R^O#IT zk5&}bUF>o|rgF()pD2cqIn&l0$rdHWEte^uZk&M%d7xlzim!t(XZwR(!8lk>3cLx2 zdXw@%)}CUySb4{*Bh@5#B6oFT(<4FO}Cw7dhvhU*!jWh+&Tk2xr@nVtK4R4zjppbKYS4lV@zDoRN;J2Ci!#)TDipydrLQdvjrmbmhXE3^Om#$$U_B! z(_5r8sVgxf*BMY7rH0u8_i!d*Eir=_3J`D<$6yy5U|KL!&z_#jYltzinH$quuE1hL zDmN4}wfFkR@9+lPIa9G^FCuAB+nh5}7o@G-Ig_Ds$8}qumNbdD(GyyhCCyJVB2j2= z7J2F~sZYnN=X})x`>c%>8bDq~Ray(UeK`7cew{J4&oh{(zQ^K*F07j?rPN7{G#zTt z8jZG5SH{SjFtuA`;dB*_jJ5s`*6uMlm+##Z{*GF022F5oI>k9=w1CpM$P~#>Es$Go) z)~-mGTfB`*cKm1~I_EZ69>%wXE>tKPQuW4? zDIc+tjxnv?3&T{rAwjd>;jYvM#4%Ow$##*|HESKHDR5=F3IE<>iY_}5=FzP!vcWN? z2###zEH#2~gWk|a(l!e^P``zgtNzW&o8UV)q~?f(Q0{?qU4}VCg@)8rdD0Z4CDj{X zq9kuI+9P8`4aoJj;jWSA=D+x3hsI%Iur<^Py`_B1KQ&@ueaBX^NAXM*w?kH@BD zcN`=^4ljY zYt$qZrm7-?)jzYzEg^4|5a>iD|fl=9jA3u`rW-)6Vs(|e>cI!8#TL!jys(4-fZz`%xGcgwpYO@6EKy?hEKPM zht7}>g_Oq)#=vmswke}L#jy9^3LNV>U~2diy*~+j?Z4$6_r-l}JY)Bfc+AHgBR(4c z2+GDya@c^t$W3$j1A!1q3j3xIisXyAs}r51=<6Mj#3U!@#5^?`m7TqJKqja6*APR_ z3rbmooJkIO!$df6Y)78Txq?4kdRR3l;E~0H+?A{v8gCw~#Fw=7O5N6w?GhjYymYu%&er@Y6{LvpI%d zYtMI_@gnw@`cw6S$CDR(RO=*EDnf?T-h@9hiBck?B#n$V4Ur_z1fxFpifyq*2k835 zB+4vv=BjKK%87l{>~Qv6*;8(G_lc?GVzicsEBKnlc{k}vBStTE-6*u1J)#{8St+wn zP%K!>)nZGI>ph&Qv#0Ro{IlD0unWO-+7OX3H1?##5bM;)Pohc073!vF5za0)uY~4k zSWm19j#UP%O-{;v$OdM!n@Cu^FjGuIu~_HEY_Ak&b1sgsUs$!fu2*hy;w*0CZ5U6Z z9w6P!Q~QK`io{Yy79KeEj3R*7(b@NAHr>sRe)LUGODQJs#l{kigrp`B zEXOjy`bo#A&SIxc7i&xDQc`HXIkV~=yA&xfQ~>LkX&H4P`J`8frn!=jE@ZG9pXG3M~s(~z8T^oubohgr^1wHh@XfQZ_IP=htZ&E{g~j~sk;Vk z!Fop7u-koXD;Z(hl$|W>Hjnn z_T&gmV2GQBG>m3)nl&)&fsP*c?Dg#-lmZV}f5Oa@4)LYxmP9Yua$#)1;Md6o#b_aEZDFnHBc>1C=02%cpq%3f%?&e@e9dpoKgxqm zpK!yvoN7BsGk`0jV`x(nSS{Kh;8>AFMDkjCD5OLUr{98QcG@6BM6_P_@Ctel&`8DI z1CWb=#tJz4S^Ih7vp{eC)H-LR$K?SHygM)l3)QC%vI((Lrg!1YOngK_D`*d`%BsEYT~z_ZlKd^Sc5OBG%?UJmYB-w8sB#enaZAH zpRc7Z&R1|tvO0loOwy^!(ro~_T4`mTRko4HlxFgz`j=XXO%~H+wNZeXh~VYy)oSYM zsyEg+nr=p>n&mn|ncS4Ps#d2#dOV_U-ad%F4$SItKRX3n$)5BwlLve6${Cm zZ@>OD&+>N#%$d~Ygo|~MMEs;&Oa0!PV6M-Le*+RRlivcMjby>m_<=)cJ*~HkLau$Pv z6azj}PhlHtU8{!(du=5v+%;?*3s z)=G`Y|N>McYc1gmn2mZ*c*;aZq!xp#Xp0pM zW`ddvXV7fYT-#Zqqw~)PFw2x(CI5PYj=|B0IYJmJ!6gZ_>TEF4&N;Nb!7FyhU1Ep( zA3IB{51YTR3G`0U32+Gx**5Yh*1L3kM1Rx$FgCfoMYs!h6?7Ks^$K=Qp(RlATTDcL zt*)?VI;1Tu)@$T`W~R>-n@cpzqC`KF2%nO(|b-~ z)NkgM>?s+MD3ehau#c5a5NYSCUVN=0n;#WLZY5kz6?D~`xmN0>G3xVemyBjSN-MWF zp_C!)=#!RaHS#F7K2ogi=EPIR6>5DCg9SY7Lmiqq>XbcVq~)!8@XM|XQK6k26@+KB ze-o*ePyMTT*tq4gV)^CYUW-`VAC;p^tiozuTbX$m_Q%Mzf0F*02w`TfsJ()5ZO-V> z#goNlk}ElkR^>5XykrmmW$*l*V~$nQIov7(QGj)E_`VXd>1#6xF2GK-H*BWIPjvXB za_@Eq{%@eK&48Og&8`szhM^7N6Z@FP!SJHRBgvE)0|pem+peM0hp!?@s*(ileUz3X zlo)Vt8!E?(SdNC=)(x$9qP`A!>YG@%1?NDIp-w_ylVYb%z@B22siWF6Kx%deBgQC& zE;GH=tk=R&@l8-%m3a-uK5Ef-mU`*=R&n-dI7lVjO^6ykc8lxvmjB{$AAjR$xLU^> z^NA%cEe|e*%l91`;@u-R3Zu;f3E}Nlv*kBk)pWIvX#0s76h&SJb7hR}j=#PRQO!eC zbK5Cif3>0c{&4Nf2qwY80ASx^EslXjM56r}clwj3HFl>D%Y}!Oqfkae)0cIjyWYEa zESAbqfDh+4sg=c$NNWp+ldQ&}vupMMRciv0V~Ml;IQ1MdnU`{zyI5P^NJociN|H{- z5GaL8PCfo1Rp$sW2$@Kl13p*2iGy3|ih({xq)U=VOXLF$N3>v|1_MtPs=vtRNWM#O zQ9u&OJup_9QGN{%($BMTvwxOs_+I;A`dV z*~79zFJ1Mv+{gI*tGdG1SktH0nSRQWzQjkY(0zW>r{|e_NltI}tFGmTr_Qut$l9{= z&w^L@SgDGws&ezd!1H5S7$xc9`1M5eA$Q-6}E?tjv6c z5_}L+lKQuKuR^0h$4HNckq1yl?d6BM(_`Npn0{TITZZTpgyYmn_yF3FIIzsd%8$6* zZ=`R0r-&{TgAj^s-FUL>hv>}iiM4ER&kXv7Jd|yIEuN%VObdw-sn>q@`#$u0G&6V;UpC?p-FZZr$N-QEJUnm$)Y@xG8oAJCa~yNifRo zF!D{fng6T-aR_;NS?0JPx|C?GR1dB>2XUn`*B3Jo;kx2I~k&;4YtN}{ZDOp8J2 zmK>XRbLN=bVkzLwIK4B8C6w<;G@r{jc;UZw3Vi-T8of^vy4&zdz0+|Q|8-TUDMcFH zL%?#UV{}kIe^@UOUn)4NoK8N?gxZ{jKg1OT@J!I~3>oxD8T7nlt^d1E5;WK`h*W75 zuEE=23|bG$9~p<`*dxC10-{f&t$vD>ZH=_OFLTTb0|TM3qp>T@<_mdj%@nwh?TrYa zeP+@TZ1tAPuiPI`_k_Hjqj|QHB-`YwEB8)1@Q5VV@|qY;8U8?0G?buS8o9<^wBYZ!q- zz<+%67iupCt}rsTBit9mo$hx-qVIhNI{F4g=Z)Th)yK3w10`=X*bwQ&s}vbn1lx&S zB+YK|uw<-;gl`4NVL{TB8AfV&ozV%ePnqFJRMd?lVGy5eKH|P*XfAo^ zVO~60d%50p{i~h_%+QeywtuBT!YRg?uVDzyq+QgA6$rg)2aGbZj?ThvY(o_g2Ac+& z7!ZbZQN?PI=~=&qBij^4w+89!3~kG!yGV0V8UTgZ0p@ z3BQ&JGZr85XxlnQB~OoAX>#ZtHac7hoRS^7qBj$x_!cyU8B-GN!l#w(oPHGVMIE@p z7pUP1Cvirz?LijoQP!Xg&+V)n_SKIE7^el02->5=Nlu(jsn?W<8^054zXs@doMLZn6?0!y`gLga;dA=*H^8 zLWs5i8*zg;!Xn_Tb4tZVruRA0d60qRUljb&Ez2^2%Plyt=-X5^gwPqy@Gq?-HvZ8c zc#wMV`T!_T!5#-(0=f60)PZ1y)tjLDgKuE-#s!hbfLt?(IPlSvNb?A)xr$Cv zbsMoBf_r*fjoCiHFDG|86z6-Fj$s}AQMRFF|5Q#F=#$~zca(_Z_;yz+9zE#K6)P32 zr!8Wd*;@R`Et11NQ@HDa>#B83G*OuP$3%Kk;*2VPC|3)A-&qB-oS=-Qm$JslSqoi% znrbRYO|p~b62n?F7neja58nLvEJ2mt{P^+GpZ7(=u_z&>(Q4wyw91wW#VJ~; ztBS4g)VVXHYOQ5|VROSBGP_`ou}D(L7oQhBqaI3%xH`eg%2K3AHkKdT$jialI1wP-#=YEd(&Y)1A`BdKXr+a<4r zx^B&MjBCbz(yo?LOtb381G^aJhyngPf3HttAkbrrT1T+02)(n!A_cVZTBFpLLmkkv zCG63{{7l-`I!n#JKbX-ML#g1A2z14`a}z0C%CI>mRC=_zrdo>b(d1LA=FyaK9stx- zB*%3bK7W!Y_wF)QnskY&$K+F#Zd;V@t26;G(^}Ine6Gc+!Q+_Xif4ol_<6qBw&{uR zS9^J%WeM)Np&24hn`2M&9Q}R9(>~Kkjx&-g;EVa66Z`kcJjO_F$4i`v;>M>i9pyQo zFdbHH0?vLmNoFTZuBYCAYDf8sxu#Hy4D0?xy^tk}Nb+Zh*9z@ml8YRw_o_g5qy1b) zrsVtz8`y^Dpt1N|_8jb3y1^z$tt$iYQkbIEr9{djQXPCouKohnF~*P> z=M&+5F@9jIpMU;tUDcvDpmol1V6HFG`Uvi(*{i;?DUzWBRk9;NjXRm2HuzQM&=fv| zl_Nu2s*H)p5OrbDg(JX@d0o<4*R$8T=$cyb2oh2scQ-POI@F%5;HPTXm6_}C+SHtG zBL_3z{@P^cB>l>|BnH&mjT6i%MzoTKj><(#oh5g6KwX%`W`Td={Kf- zA&h;P?>?wy)=$xxKm$UV*iM;>!X$f?uX9wWgl-T(J7}aCZ1oG?>hQ%PB?+%83JQwu zgH?*P>EV#HI<mj&K1!%It}oenB3Z}`{(M7vFHBF;`KPDH7!<#wC* zR0RJp$7{5G)AJWJ6G^qyr-mwtT()2fW*|y;(rEeJFVcr4!UrbegCE(8A;JeIqC0M| zCui{UN5qE^@|zs;n}}c93Ill#`D^*`s5*VrrFng0pxB@l;|{ZqW#`zT>Rr;{;t$71 zs_29~aY0}5OBmrAXHWWoa`|b%_>lVH9-8q>yNBuvb<~#t;$+CUM3>{c=ZNo$UD~Sb zCH-sr>G7yt1%X)=5yZHvC%<*nn47ul(QiGn@x~ULDwpHgZMqb@#*sn{N%Mp(4AZ0U z-zx3*%-8J%ZhB@;LDI^uTq?Se6*&@qttx9~2_dnz z1!xNh5R(!Flgs>Q+icA~)Ps3{HM5FVOXIf9@dpN9`F&DZ3f!52#`3{M)ypN~*e!uQ zQSf?sTX}EPSejZ!mkq<&QahK82!XsGDJrE&O-9ouf#Vyg zHL923%*isJv5dQjcuK`U<}zEsW_OXfD#y*~hVc61zJMi<6rvsz2nBhgY9APfkJs*E zzqno`%J2BTpyDUX?=!vFdW-M}nIE{{R(YdOk6uT}ynrYY`camQqZrk{CzPcMpr?)I zg*V@o5)=86+a*j#yd6vfC=MA06IKy+^528MF`Y&??1y zteQubtJq&U&=Px_*~hmvyYJwvOW(U)3*Up9<+6#KK7{ss9I_G9%l3b$AF(3s4-txo^EJbC*IoVaj|J*lg5^CRg6wB65P~~r z@4mhu-?m#rA%5b}(!}#yA)c znZA8rZ{WO#ePxe7eM$V@*z!;>mOyz(H<|*}H_;RP<~y1K6}Je1?}Y)=Z}s^4h*$dG zejJd#+uw^`c;aK4sU;9s&%dh2@6I!geu;GYC^xPYYC)(7gd3lleZ8CkU$ZCQeV!Z) zJ-fql+4p(y$ept^X0rpfLROPikeov3k6{`7dR5@cu1?3wF z7EZiZ*?D9BHBe2OIU{{&hFaK=R__aFwd&EQRZM( z&*E5NqYOaod3H$TyC{lElXFw^hE>&bVvom2ByBEnyAR1n1B~0B4RZI&<%^Kp!838! zJVg5!MRPZnQ-1<@n&|ju14yY%ZBI2=f01TNLfWC|wkLr8TIi~2*l!nY5MNgxz@)Xh zkO$b;Glg`_3>`9_ptp_uNr{j+Z8j*o96Fht%@pk@Axnt9o)w2B6YWearfFi!+Rs-E zi6o<`F0a4UY1P{+*}KwVb?f5eF0nNc!+HL02k3KumYB?vAKWu;2UT9~^J5s32RO5Z zAv9aQOx?;bGx!ljfI5#5pQm+x*sir`tpVXyP*t6$AxEEZhJa(-?O>))(o{ z`dXz&0JL40lIfSW)kf2QfFmLNCa_7>rZ4wKqxZ@=L&`1xA@1n)V;jQy_~nPMKLN8Y z$TO0jf`+z79Z8Ng($Y&H^o`m}{mt|C58uGNvv+v9R=6>)@9*m?a2o^#2&SO&kKQO8 z;P5?g5CNE-4bv^A#YkwxCs zxH$-fOaC}@B|S&f^_pg zY74nDFe|wbC&jcWX)?!$1@k+|uqI0!EP8D3FUX{Z#MlP2{5I%d=kvMkL5q}96JD)n|O8BlZes-W8El1{61Bu?u+DsCRI z*{hekK__-SVuNwL9I`gOR^^^4nc568>YR4FS_1b@>~d)lx-+S}2N~`#k~?g6FT^m0 zg}s|_p3IqNp?;AJ&KaZRh$Q2k+1hMUUMB}qy2+b3J2pIsnReP7-WMxC&`fw~=?4{M zZP-(a;fd48o`U=(3XvVRoLoZEY9d+ignH9{sclt!SVQUSC)UIehg2~NR(<(cj`i@QpaQmmc6>0sYq6Z!RV!*fR4>oH- zg#01M9k>|FZ7MLY=z9n7JK^mTFt5zJEZFno;5zip7`=!4|Bv`0Deg|G9&X$LC z1A*^qw|Vj`Z$~nB#Z=mpeb}}kyIYY}5E0L;=0kJLDnK-3COPLyRK@YI+Hyna^E^IsRCQw%J5xr+Bd3eq z?&C_kb^bx4$JW^Ogtj(OxZkd9cW6PucG1>wG5i#wfhS z&wSPE*kx#YBYoPAlI`k08i_s9LLb4p9@U(lW7il>>-u-r6^&%^_<%DbSx-M! zGSjXaE}X=ak?Aun?gxuTd-^X^Zk*3pES4C;D|1f#8MUg0m4oUwo%3IgoL2@Tbo~hD z*H$|YD?VF1K~7TCem3?Rq11t@agPF#Titqic($amj{>Gnq0@I_Ifs> zJT}t4a?+%M9IL6Q%{Fa!S6+?^+D-GtvefR~C9>=p;dE^}=W{zHd`2blER|km1JI)Q zM$fGE3qAE2Mk*Zix*0#CD{k&X*xb?%{T|`3QV9iQW6!TdELXqHCz< zc`Ri&Q5eX2S9yB|LhpGCU-|UZwo4oR z1dre2FJC1tTo@Rs{3aCMgQs4o@w$S?AT_7uC4PpfB(dg>!+_5xtqx9!Fau}c`>@f5fp@GOUu7Fyv9ef z(=CXEQ74pPwiU6yrFHHsZ6o!x+Fhf{CZAf+o9=O;D+)>I?G4jtMuXRbx2}S>RaPV3y%u4;$Y!D3iKy7gRP@(CW^q4wtuh9l<;yK$ zd;M~Y)iBGCZoqQ3<@WUH)4Nv>Xn0&kS1$_wLqXw8wP)|C@UpUVR0p3zUMq+MAREH~$q?3GKc zYc~PFc?FBgi-aL#cwBeF8fKfU?IEAnw6!F=KX;6fpUKoSST&_7rP)ne(^}=1TtCKO zXUQ%*LTaaY;M8?2WSmKRXYV%V#%V&2s@fITHDA~(R+W~E7mU4mS&)kt&89COwF-{z zR={Qj3d90YQ-$5p6H><;rO_E?Y^7dwCC9$oVwig+MLFY{p_uEVQ z@Goycp3Q^D*X7p`WFxfq7&}aeXR{YVbGw?1XYDCoZcg=nzUkv8iD5BG3#lLysjoDT z+ye#aalkl6*ay8EtA*{3hH>_jM7Gdy&s4ayNNZ^B@AIW83581aO`ZMe$-H5)GyMJi z6&%OR5?byy3(t0|OdWQ0cX8Op4wgMOksPFXV>=So32Vr1@|R5h#;6(lzbNP##=$`7 zG+{>I;UbcVrJTv!+?}Hw3;_Zhe)ylTpIz{V^n#4ah5SIiKw`kaz>#3doamSxU)!pG zQGb@9LlJ#vT$AdMApf<5($x~~O(lHyMi=M>l!ZswK>d<~QJfK8;MX1@DQg5a2(eRk zi10gM-(S=#yj;2>Ij1l1V(qRnqU7MAxsZ_Ab_&mXPnv+)c~e8&~Z(|LFY} z)CC&!?A!tOtyN+FSEb>9n-=%1y4pS6m<$;+{vxys^%LsjSWmz@pS#QVC zB1kcj6|Y^XicBm=?a~naG9mo@1;(CR5_RD$n$nIYvofBzeZ2gBfjNXIBfud@zyf|o zB8hBX%L9ZROg+>?8aBO6DD@dYxLU_fRtD+ z)U@Db3oOOQui4%LGHx9y$?Ms)oN%D<;$&I(9mjTavbW84d~4qX#Ms;h>`!EwjoKdk zX16m#5}%BO8^%g)nvX-iC=@eJ6ba|sC^1VfNBU-V)G{Hezmg=4RNJ(^Dx>p{0%1dP zZ~Es|z*F-ROnF9nf_69Zr;arze@SS}l9?N_$VCL?`#*Syh0>vxZ;jIjZYQ-x;pDVI{H_19TgpibzQ7#GvhEGm&Cih;)BskRTrWXF{>bWz$*3 zK4^fDB*eqa1;!lnnu5Sy1y2W7C%L_gWDENoJ(kvqU5!4FsKZe9s8BgSsb96lx;s&rc2q1K++o10C?-w#HbXQy$0A zU7u53o~_zTy+mC|v`)90rPt(Q8(2pv+~5vUVT68NOT)_%lC-z(%(Svn z)0=FQTa{jOywD{n<#`m>z+GX4e(Lj#i0>iOnLuHMfx{;uXa<*QPb0py!?@FRv)UsdSF(So#$q z6Uwl?T`@okXf!L1>S094se(>vkP7kmP|hOTBovZU(`S6{N@jU0S zfE1O0CldDx7WwH_Dg~VaCX=)WYdLuGi1(!O7BTKVpcnD3AN(+`kvxXVcwxCwdP0I( zO@;z zx!*q%W3J;L=V0I2v^@W%_4R-9_7gWWcCr6=sxL=P+Zk01_0!XRRE9h$nD*%h@>SLk zO+-;pSgSw62F^^;0iwC5~I^v;lle2YI8jtzM zW(QcO+YQUj|Dy?;FnQlAbI)Mpk!_A5Tg~Rv|WZJK< z4Qjgu8kn#ABAd;=7>^;xaqKVGs8B5@+S^)CMP!BAc&fJ4G4Qsdw3GF0LVu^0JG9FY zFpZ!2Lsuk)-Ak&G~$9btD+!-GU9E`WR1{&gz=k*?{ zBl%3hWF#dZkxsedJ&Aq|uAOg^>Sg&Qi<72_@wy+a)Lmy991Z|E?Jm5FEg%$6y{UD`mokeVWMuHN->&pd#Y z#*l(~wVyLi&wvHNGbXEY-7ep40BWVVrAUqEVOwW-D$Ds@3{8HOEKH1oEGd>*_O*&m z^$*6f@E3vZeaO8)!$Q{N5pKh#hw8emoawdF(9#)`5)>TJwcC=LG_#-0a-reSz=k_r zXK?j=Jd%@Q=W0uEPr>dEXs=l3GZ@OV@Di&z_i>j#?$aIJWa*CcKp*fRLiS)?oN*R& zPv{5kXA>aZsFPTsp}5NDx1T6&V=J1zl*2#0vu`Pw!g1y9alD}B zBiE_!&W>_XU(rg7vAD#%4{d%(jCdh-&z>o2!3`$yP0!ZsVyYMIVJ79%_zaWy_#xSg z6xr{u%u<|St$o0CaU55|b{6LncPsOh-vJ9RF-zC&z1jfmu4eFbZ<$X-t0=W5z zGXSeC?(i#kXW-k24SvMBL;2$9(nwk-*3>Zt(7JFw7;XcBx1L zF3!STm=M4x(9fP_1CkAbnJ3aPm3M&90Bjd$f-|f|m`7VuQQGmkR9$oS`u4Hz{$t3I zBcvNs+L6AtB6R!$H@Hy*^ z`yj1znE`vxls^Tj?rTg@NzwHj3aX_@6$anvE6JOm|8Xi9^72RITn1OJmc=*vT1Tp` z^Nqf~w78_1?wow1ubR$QR;QADYyY6HWt8i2n6d8iVHs+wZQtnYRlMa@rLl zsH>WCwfK9jii&|&phcC>#e4b>Vh<8$+oQ&c7lP_xiOq(&5A1|i`ao9**hY&3gn$O- zU_U*jt;vY*nvt{DVL*e(|7X5FH+`G0fSlBgZ}WBSZ?^)sp0V1$%-0PD*nuf@&RNS= zl(N%uiGR#j;?8gL_2Qh`{@Z-5aLM{MUv=19E8%5W#$Wz1U$f5tr}?`6FZ0#-Kbfzg zK&0_=|7N}-mZE*{BRG(+CPE*UpC*}RpqjL}W}f?B<|`JV79N0!xcCMwwiSuY2lNpt zrY+P1cA9ZUi^Av`hR^y0?b#S_Xu3AxJt_B=QcZx(Au7FbFgtEfldj0EtCmFHF&gMu zaBY{otXWz9lTGQ%v}e#JO+e_ZpRuP{4%r!G2a6A7f#jTVl=1_453XwUy;JF3>1f2B zGkH3iA^TSdzwd{A-La+2`{!=naaI>Z1a!99pkf2=faDoVQE}l>ME34wj%e!EM^`bf zt$`^sCx9g|g-hmo{@{1xxyVH~J_m{LyR<`qaPrKF#6OR35LmRN_WwQk`frb~x{%s` zDuHvGp2M{SJ)-1 z>n*F;ZP!YylnO14an?mu`aml;(_DB;&*S(?OFt-_GUtvzu1w7wB#WJ=yw^Kxx81it zrg=Di*bLeOmkqIi-RwAX$AWSS6z<;xJv*qdPlG$a>O<~uc`FXzH*nz#{?Jccgu(UQ{`JP8FOQJ5$9w zzH{XFHzm%;t%W*RFBo<=@z9;3N8Ras&aK*t-z|u}K7S5~e z8Ln@ymJS;F$Sx7(was$$%EQ8G;T^xo=SQ}&Ke4(=Z&|JE-%k4pm(db>_Pv5Yxq!j) zHt$Ak7jlt|B@e@xEC)O6C6dF;PIJHw2e{qM$ER7`T4psE+E=5kHq=|gl^Uvgu%kJ| zw}Rv}$1AZlWzU%%*my=&pY2c-=~vh@SOCmfNC{1c+ZOXwb3WeL#|#y^@x@eB6K3N7D0oCU;c9p7a0bVQw4p1tE}AJG4ze~_Vftits)y5xn}Z6ng(7;Wab9R5 zPk|y3htKk+Do_ZbRm_^(#2C4qkcj~&{|Z}T5y}S>&6Yia#SB?iPRku62^r5R@r5b3 zxa5502-=^}F-<5`FJSm~hXn-|o>z3k_+B<-=QmM*cv-P3GgTw8)e1cZJ?5Ekau9^T z@N=_8R94!t>Lpxtm}xZ-1d@{d?{84sm=-f*s3RO&J8LJhTcg9IN_zm54$56CTvw1J zoa&PfJve{hB=}j~Y@dYb3iSXw7PjMNpUBD(aL@5XlCW}-0PBo@47VL-TLi&mXo!@a z;jOVAc_ZZS_(9kziIG0}LG$sHX%$3xiKP6?uCi<|ihT~Zx9-~q2j+~Qt7_NG$IvV{ z%F#G0gx=x~D}AN6SReue)2{D-`WOJ(y~x4(`?_2DQsNWQPho3b@O3=Qw%5&oYq@&; zvS`T3JGGfQLgk7}t|i(o=gujy=@OhQV|s2cHDXJEbNSfH`IRRS>XSA*f(`qbT5HR% z#=G+0C_AW=KJyP_0?A`?KgSZs*5y@iBi}7I(PihimSWjeTPF4*X8&w|iW~NxUR~}? znY=^k%7ybt%(aLeLQklS;YN@ql3)k6FL81yEIWmf0LIl%PYPahH$d$~Y!WacIruPT zohnqyz6yh#lZ*G=Iht=%G2|wWE&MjlE^!Uyl;Ykl`L_*7~J+TPU8N0t|Npqb)o5rsWZTVvSO zS|8Ayjq8{1w;(aIl3RyyDRr`&8JIOX9OZ!Z!Wi_L!aWp?N+huB#EaYoNm)FnSv@uY zE_oF~;M=vE+_39O@uuRrvzLj8(!{mboA&YJ;FCZV{d-@Ldr{Txy(0E~@~IYx-gxAb z>ajQrGiz)R*9RJo`Dz<}DuPJid+O-``8+VIZx!G)!Hm%QAw1D3axXmCdv zHjthD#5nd^X@nFNfd#L+E+`J5ur?OY_`Qv>3HehLNHp-IWanCmr(p+JBdp~OolKie%qi~oIML#n7w}P1pGBzE! zl9Z)PZm^!L<6SE4A+oyiDm56FuaCFV9^jj^z>hL_UaHt{H>mlU;UGFi%)|c-vSGEd5q!ut)Jw1F z*QE8;ov#n?@z@;Yh}AQlIJK%c(2Yi^k48BOpNwAeRC;IWOrES3L2dZe6RRNyO2drKpcbr8BOrY-q~?nQ&_lA=Rd$7!^J^Xrvx zl`!|fx=*+&G9Z)@*VwQqKTDy-FVv&9;`z=fjW64Z*JvaRVdT3apqPyiS1`v{5>4Y1 zp%Lc;lf$X@D67ws(%fj`E*C&b!D)W(MZMf)D#Mp~oocTW)w4TT9Q)L(z0OfW^gPDb z^G!+iHM0ixkL}&JK-~1Nrxdfm!sY{F-h7!*1)f zk_iq`HBJ&GGdqY!F!~1DGGnU~M_p)%wle+7Ip0zreKRDuqRWahl8jc?P!9Av zJv4mGnDNo5sEs!-_h@*e7`8QeDfqOcx+ar%o6gD#HUBU-0xa(<8cvew|%(m0CIL<6f)7X118@7f1TcAd{qa~4Aq8~280EU}-djRIOeN`UgD>&(@u5pF6o>J}GaF9J=*@h# zKviR#uMAJE3|v>X_)IO@^#!^hy6z(BY<2w}@A^#FN>_^Y{GGtXIA0^g{F+RG78_J9 zQMPvGDer0A5gS;iRXf=ZcO(IxL8jn)SSZTmxD^(j^*nTl)!l>Pa|_%&I_OzgA91~s zU*oY_+soS_`LGy`rHq7PlFB6fLP~?)xRN zSlt0bl3U%X&}JfX9VjZMbT~`=0GH;0W59YqYiUr8O|7<;(@Y^t6O&GW!FZVp(Z^M4 zzQpj17Bd}0`i?=gqOb(CBGYw@;gGmv&Cm*OYKo+NeUkyW9^R-}yGr|PH0a#7gNleHJS?{${DR|YEDm3J1<&`g<`KOz-!@E#l( zR7NnizF+I2LmwIvj9?Fkw_xYJcCF|k^&5aOMr%$#jN zs5BS$?K9=r4oA{zB@!83u*nvyp`25VGE4LXUR~}{wu&d0%41+MPOei{SZBO6x?Z-; z0`IvFW`kaqFr-~kxZuE~p!ZrHmDY$~q=7mVh^y+cCPDW5!G`mIV7jAwj|W5zeHzu0 z_>5jHCKd4P6gnsj!aSKArPW{@?q_o|yi+*jUa~MXg5{)8V{lk6Gx0n z-F1>kd*gKfg-xy(yIAsYHN_>~_1xXWFK=IY;RMMolbf`@-lDHpP0bh!;Dd}vjiF1m z7EcX1y)&=$%mQWW#j)hM+C1TaD$N}2cH4YPc=tM*k2pgpc`F{I^t%)oX>^@*C!cY; znk{)0rCivJQ!e?_2m-*`L`lU z2?#4CJ$;M6PE)H?y=SB3g!k?J_QCUKbCe{8LLAK1N>Iupdc-?z*gC&@->~3&Gr%PNj3~ju$eD7L(Ub9@} zY`aVQhH!sAgRDJ=Lhd=%nf#g|mx8`EQg(ocl`_VJbY{vC_(j?6sSp}pN&w?Rz#8$J zv|4@^Bx*1wBWhd-X)RU3YZa?4QD#o~zMh^p2=Cx@t4@!yGAONmcdIIPD47N0K)f=J zZ7H`(*v_dEZ<9ISI`HKAbGG)ju;eMMm0ca|AhCWdcx)KThKXpugt3#bDifZv=#}s( z-^2dVl80!)aJmnR_om*Hh0mC5``KoeFr%^PCj=QPt;LB@^x_+dMbfIsPw3Oz-N1v(Kdn0}{vCo;?Eo$w{dkKumj02xRS1ykxOGdKG z3XYF*nCbP+GbD*>36LXv>C*)2CPi+Jf1mPE6~SB5tZ{f`9eF0(yg~Wu{tw62?il8*WSr_nFyjS7M6Vrn~Q1=y?H(jh@zK zW}(cVQR5O9I;OMF&8F$KaXig>!Yd;h!X6PaH9a#P{kD4E`uk%e{tko_B zZG^jY$C;8_%3kJMqW!q#yLq}FIS`EBUw$jC8xP-*^d&V32pc1TkVKA0j?sAv2beHTt?&m_Sg|RMBPSlqtSY~l@ zz)(dUuS?mQpsCpf3z!zCNLFHc8Lli*axK}R$mJ1~?RX{Heue$fFLr*@8GD_v#Y|f5 zUOobRmazoIr0dhJBWF*>p^P%g(ewAgnl||}U+*RrFN<8scOiS@>O`DG1{a!OAv12C zW9S$iZ#Y92Z=JJVw^ln^)NDA*AnvQ;>BKBrvs`Hxl<2}2f1@7g=YB3KxJJPgvU3+# zHoZ-wrJ<*z#A}OYT$HbQ86!&hF>zsdF|Yokz>V{o-p0YUu{sB91>Ek#!!^5?QM0Me zphq)&;YCEd6a+g8Y*(JXhn=NW;n-AW)fpq@Yykz_vt`pWSqpFZwOz)2!&m7Cbx+(H z<#(t+PS52}kvtE=yNV5c)`LZsc8p-}ZOW;H#YMaw9g8DLY;YYSP{mm1ouZ%r9pdR%MkQ7YU6r*J zJi;@AE?55AyRJzAW==ij}w$~)0Pua$JW~sx%{gw2zWaiRjPy$6J;7e8h_~5_6|&3eyL+D( ziPtgXB{+MYh&E2NiO}dR<)kq@-hW!^-q^u6-+F{!tNJ#P<`;*SZtFJlI?OL?lpE_4!m`beCd)TVgLgch9hSuex*%?JLuGY7l#DOWx53O9%PjRpeQn$z*|IgiD_L$>)76 z{<`~^3BG{`^GL-$sTQc^8uzAbvCA)P4;48hUO%Ju-{bsRv*z8Q8x$3U&U&WFR4m0> z(h>z~zK131{n*>Ak6PaMxx&+3C*t@!wux6WR}byvu4e9Mf2tq?u@|AMih!cphTgci z$2NgbJ;(EDX}`$nEYaJZq%XbeLtrpYG;~N2yhGSJ#$l-3`k|Cs zU~^&CZd)JN9IA)?ob2g)-~MryqURFj;WE-|n@6i(k zEY%{k#0FJ4Uje7p9!eg<$mKF|5@F^^>KQT?3`yB_y-1ZbX`)1+aGiqIhc#EwiP$?M z5S5qkAj*54=Y|dazAN?Oy~p}#>ICDVT3+HJ+5W}*vL?0YSwRL)_>g&bL_%|Phc66E zQ?NB#Jq0@W$LH*e89QgpMhW!GQsWo1^wxw;F*K@6v_1VbBn17_xNW-m1!WiG`%rG% z8VLL3S<5t$3#K1hSP@?tt4k&ul)8FSdZ5iZ-`mq;twUj4CT!?!J-HuTvdS~Scl2OR z7s11m>RPY%h`QF1wJzernwuSmKBv4G)@3Y3mNMlV3uBbY0Ybr;CAaoZz#oe*Vu1i#dn$TwUK!Mqs_qbpdXMT-}l7K;>eb_|8P3-KS}r4}$QI4L_1dOVa^oM!p~tbo zSs$Z=KIuhWU6pAMWf@^#c%6ZwHmd#S0a1RqTB_B+I13W{rmXO$19qRA8iK4Dg+clj z)&}PP-AELr^w(w3^PSq(*jn@z*UWMdP^qD1rSoajsRTObM@4s=x zBcp-mTgCfm7-;M9&>-;$0S|A05oE}T1Cq9RDB;|JTD-fx|4)I#+KtwTSHI>4rbcrhx`oFs)d9 zF3^QZfL{v@(1l8e@8k%y@3!f-i*2SBnm@U>0nctZ^-7N;rin$n($?^i`pa z(QL(r&HDOXjTx=1d|v4$98=ad;3JAIQo;}p4BJOb_XfVIdmt;(5c`|X+--ALU~RvW zfYwbx;}*NZpz($E*2jGzeZlICyIM?!U37{lKLYRTRkoI3@>#H8EGr70EF2@(g?PmNdfQ{T69F0zm7uRnNi;gaAFF5U2+OPc=gai8*VUt0^^SBNgXb6;Y23HT~}>nri>J? zq$|!mQ!^|!J;uuTOv?2VKX!V(fa_1ErWtB%t&oNGDlh+7hD}UE;SB%=KWD%y^so2L z-8A_5kDD8rT7gu)yZnEzJ^zoV&(-ylVxXbQ3s6}QOMg;RLKWV4P=>SgAy9zNLEBT% zRN`@Sw-d&N73q`a^FtYFIA(96g%Flx&CDp_yW~4ZcJ_PU&x)QVdeTG$i7QAFNNwda z=)H@YOK4>PxioWZx3O%)9q56$ukMeU%RSRsbAN@Your4PLr;xgqnX#&u%aS&EGo01 z0bBXQ*oN0b#__GJG||t|7NgkG=InK8%F3)O+AdFdt{wgtkVd`W~>Zta1t2 zC=Ia-<%cjk8yv8_wjM6XLk!~VmqYlRk2U5>sp@dwv^VZ;K@3Q0`TAojaB$3Em}d0j z74L`m0sda<8miGO@p-BgeJq7&zP<|DDk{{BFOVGHl4!9^Fo^V<@Q4~pYNz&ZSHL)v zPieDY&KAf!x>PnAszA@+Fp=tGq6k+kg!?fE1~~iO^F94${cs5_c9q46Djp~sAJ@ouyd z&}FNWY_rs@{xcTQ!IaMxT6PI`pKv-laZxNX%F0TJ_|BEnA7ZoO)s>6%vWG_WDp+lZ zlZOb?4ol2$i(C%#hCT9-g;>HKaOf1Mco%fe5_i{G;?A?v7G#vx`Gv{V5GNGAX9r|% ztW`*%C-o}C_%@cI1-jGj-rK~B+SiHGubnZyE$*r}nSYExs;LSqSeSyFOf_c=4Z2GH zilyhBlcjQ=`!->NayRYC6qD_JjAQ?E`DTO$^5iab6+cIdCH4dCxdn!UM{|q32grO2 z7^OHnqpz8GM#I>}3lLPh83)DjZDEVhPdR9JsC(p(f`gXA+0#K&_&uK!rnxgL=yn*Fb(Z0xvPH z1a4_u#$3DJWR_okv@Xo|)w|)2Dl=8hdb51s>h>4;Un{>It6nB?Ge&fg$Hrc*CED-R z@6GJdpUoWUT+Ej4MfypyP&|a+Zqz40SU=a5r*MTd%70wgHToNjX6BqS*JT-3VtNJ8LTM(-qYffkq<2U=oyogc`;xhQli3MOOQ35%z| zj-9`aQE2i^*OBfZAVge<4P6#YMmsF<#L$WCDL3O;Uqw`X!l?Ovp;O}Svy&zB^E#aQ z<^UZxjQPW*X=jP7`3V?v$mP8EQLM9BpqLpf`u_df}BcB&W)P6%yB?M2ac(yc2_0z=aCUVscQ<*sQd!LAga;N}i;N z2Ax&)?6e}m-!bhphlaAFEzQK78ZE4peN);|%*3e!@9dKV{&EXfdBm26NKsYu@JjQi zFxzbL0@kNQRT+YdJNbg#Y!ITu&1VvuNtd&F_F}YxIQZwDt|Ah z1*~sgI-9gVRBdoiz@xN!853t#L6MVQkVvy7o6Pr_-aN9}(DpTh3n#h=HGV}n7~P8>Vau`02cp6(kH;fGn8t`E&9BR1Rdd<$}jisWl9WIfRzT++#$ zmkx96Oa|1eB*y*YRAP;S;l~N7?-pnA8ocK<`&2eEG?oh@z1QWmjn{XYog(2xn_Q#_ zZ+tL@mZy3h>|IM1$u!aN*+5U53^F|md%QlfHUT1zLzIJ8E|$5o+9c9+r@{(Rgg%H? zo>xV>(14czwZ`VBQ}MT%t70%7i96{=?uIp;)98CL-u=FQqn(fmZ;X5NiBy)@$LWYX zysU^+bmLH-U~L);M)#Jeh$jIX6Q9JuB9Xdd_PLv2ZsL4eYhyf!M?njFC#Wx2gj$AC z?^c8U!oBAH1T{Thgy))WCE_E|bR$*jI<}#)={EcK(J*nqk;S3U*3k|Zkx;)CdtYgE zCkW1=AL5fVbxiYJA@htVzL^QnFv>A-r^nlT z8%Mw}d*H3r#xgVpl=J7WvLeDpfVTdod*ZK2fLEO3RzhE)r1>xt`*d! zZ9W#=uPxdcJz>}N(XbVDTKA$Kd{_!$SMICCE-rU|-+@m*u$AYMPWX%}GtLd&u}~Oe zUA1(g_|@xg&B@KoIs6?EcJSDHPvFH;iGsa4U_+_wpFRy;wHibSZUx!4Em;M;=x$*5 zT!NhNSdS9Buqw50I)Xr4*W(}%TZ{nBrSGfyo!$vtX8H#A*h!atlLa%>1M#Va0N=yq znBi4w0l1N&;5aw~LN2+x5k}94BCQIRm4`en8|bRYMC$N)O|pE$D+JVDOWk286hR2m z5r`>Te#og-{NxTnb`4t>f4(Qu6;FBu!RDPOe25l26wNSHH5TcD_}Pi06%zF}tTftw z>p4R*I=R|vY_Gid45emavEED}%$Uk%<~p zl26vma+4$$hZSPySSu*9Ks}bD#9U^>J%cDQ!H!Q52|jz&I2L?a)GP2=mk8}~=c@U- z&ssB?H7f*n*81j)4fd`WGjb{>Z0*;(u>r89sBbDvBZl#frSk=-C`7$lcB>YkPjI_I zmodApViymsgd2Z9pA0hC9k+ScqPZAjA0O(1 z@=lW@O!I->E2luXPZ3DxvuJmo9q2*4D7(`n*1U(Y(sb|qi2+O^|M1ii$WHww&7F1L ztY^Bwq0VSL4;EZvE}`fbtR;vBEV%+r-Kk0Gc7>QH+xetP@F(7cgyOk#6z48&A`V{c zD-v;|t@3Buk>+US=u0FN^VA5t$ftjbp?k8W#21|pS)k-&qJm-Bsbz+z*mD@6zbTH- ziYyu@b$w0eQyNpg-N#TqS1Ku%fkHw5dHrLV^t4GFB}H~%)8v&lz0m__!fg<gTJMsAD9igy zWh_@TJMnGz*Rl}1cT-?%P36DO|(SHENN*X^`8#r!Lj(-JM5yc2)UqI_jSt!7 zWlOG_D$XqG2exjl3MC;2;l}0&##p!?$v#oK$A0E8s;=N%g&ztp>esN77#ayD855SW z>gimrDakD4C;S3Bge67ollArJ5dx()D)PIMnbtXYN{ zKQs^0v#svnUD2<$KYK@`f1<6D?b81a{#54(ZS3pJ0 z#Q>3!n{)fK_gs-|c+AH<^-uB(*H6B${bj`&daEHoKp^4WH2l4NmPf);&ln{5eWK^D zoIOHH5kmmA>stgc+igKkN?Lp_$gB=WhtdHnB0j&HN}fQ!;Ky_tRwaJiL6QMu0_JhbAgr{ne4Nr=acrAv+tA*M=8(yZp-IcSNCsie=u%FHn z@GkP&bTy$#PVE$0;pjZl%^PZblDvRItXcVHHLsqk>@gl`ev#ES{pYdeYs7lV7JuiZ zmt5QF$CmD@6Oyi(sfBQ+E69Z!K5uu&2_V;HsjkYs1TbtXC=^;n`kjglJ)2JRw4D%- z!aAlmFYpsrPOyY+H3c4HLZeZ$tYOZyXhRzhwxRA-pmrhB)cAfz92#%L?c_3-{-C=P zE9X=gUbVq-N$8F&z~>vq^X+MQE}IOp;&Iv={vClN=q3(09OUP3!a5~aCq|6zU zbQH2uwo@;$qzU-EWeGUFGk}W}=NFy`FJM5sbBLKgfvm`)`Fan_MY0AFrX-N>Fb@fC zKoPrr7ZMAk|CpaUnC)ap$IwCJNQ3RGWCn5jlc=CPbWRooEe4)oGnZ@4mTJ z&fc|BIPJAp5HIIK7((AuimyP~L96n1`)2D#_B~0B3k$t_Y&Rq8fUw2geSNl%;I4pi zDTCoUDircI2Qz}uhTc6}Ck~I9ocQ=|3)20-nY<%Q4EoVmA~^S$o^NF~rskg4p{aKZm-C26iBjm7Jb|`G5DHsG2)yDPvtf zU9(-cjgXe2LXVGzgr*u9L=C$md5_IhQYfN|E%f+KRm2Kq+;bKTBT9EqODs9J^g~+4 zOvpv~G%T*t3}RB^ccgH9gO$f`EuRKOQ#IrMvmeEvG$c z_@dGCjXRuf%7-tWL?H|EL}MWmtd#xB!#pJY5yLzblwUxXC?c*?A;vcR@vqBzLkt(x z?|$^(B)O|jG=S!}%$FEIBxSGmAsNS1FoTTrJ+TU7Z)6Itb5FA@7G(bPM3tk1rPgef zk%3fK-Z&O~o_1Opdz`rOJAY{>Ha@c<0Vqr!%>sfxg(1CwN1w6W-X9pwM<=VjG!kzq z=I$%}v>@jzRJyp48bvgmbR}0bK{{?~HDAhE$3Ddo@85yXt-tvJ1V2~wNtZMpPsTQ( zgQ6{f`!c*O=vlAOuL}Q_YX$od5RTG`|UWm~<7vuNC zrELqrn*IW4qej;qaFnc=25|SEmbj`u>n%giPj}a(zZaqyt(5a=u$5vYE)t7CB{m^I zk$&=18JPitMCLp0DGajoxQDz0>P389$y~{FQ>mr-#*+$C0cFbc%%Mt5m*EFPi%@A? zianNBHE%4=w5IKfl%Ip(idnhd-R&x18|<;u%QU&r6V0@Ik-{GC&zz6gP_O{kW1C2* zF+cD!lap8odF?*y!$&+OZ$3&f6RF5r^Q1WGymhIppBTuC&&{qiAbA;RMishy ze0yS=>mo$))62|w%z?zGck@MAJ9uo+Pv)bkM_*dglPNbb#BHr(wN3kQCZgEz#N%3M zWwOR(35`Tieaem6m6A`Uwo#mk!8mlJ5L0v?FN?)I4qRIF@oEufeMB=#5Q5sV2JZuC zGwoB8{*tl<`+>TaA*mN7MvS<9DO=1te}6kRl4Fl>EK2OfvSkaUc>J;2=Lo2Y6)#H!(>WSL&01wuWQDot@gpuOAmjCdkgADL5)C=079Bjn}y& zSTx{ZkxhZ4uJg}F3gVlj(m1D zlh<<_&9@sC)drhYi(!cVJ=vAoLOqdMH7>3LpLd)$oUrzwUs9@fW3U~#*P=F1=q(^> zH8(Di-FM@D*8OBWBwAhY@o?>}6EMAy}U2)pCiDkH_2avD`@s zke+Em`@P;7KJvZ0~5qJzsW7GZ|#4^CKA{P2Pv1 zod`lgUg#jkje3-FN>sIV8;)S-PG*BJCqz{k5Y0C} zXRl7&iU+xfu25kcJe*076(}%XE4TC>y>28+rZ?KUc#v0gLfC1FwNX2!RO|8;?c=AF zWX-R%)ebM)aT6Z!bT8ZJ-kZkU`lQn_&(e1xl|jI+CLjZg;z5C8iN$4$Ks>(eGV>~CpR>VN#yKJVWhcb7r@$3L!U0JYHKXs?19D{y1v9*=Ja$|;c{MXA z?!i`qvx7H&3Ah@im^vEc=|T4tCB6_x%jYc>OxDU5#Synsp6}z#2qwy)!n?jQNmz(E z22M;vVsJ!dPNB$4Iag)wHyed1$U;FL&HY^B%bM8!?IEw3SAb67NHJyBq7;YUXKK9T z4VueID3q@-?)Rig5o0_nVaD;)tab0Ee1$1WSuydi^D;Xja@DbtmgmObrO_zLu#~Et zszF{;V*fTIwqPJC&9D5FXq+;taLYg`U*-N-Br}W6>qiNO%0vclP~5n+KVfyU-L=9#BXplKvI=&8TBgG$c8A9kzViWxZ1H36 z0QJQ>dap%J_0bZ(@^dAQjozhWn?mk@sdSeYk3PcIMAXl8Al4?-&jDxSFq)O(7%%Z% zi#I;oYv8GNQzey`P2sDOUTwz;5(*>bWj9+^~s#kxG<9s zu7&69Jd5V7o$OpjW-aM#K_|Ah9hes3pMfT};#S2J?QtLRJeTxrP{oFucUyf0{CO2* zf9-)tJbQVnO#ac1|APYYH+lp^V;5p0n$VZ_1F&5Xc1cJpS!MhUnf!b-2rFxc{0$WR zk+2VS$h$(i*ZUw3Vj$#Mdm$&43H_QdyDcDdxdnuuM|`Hux|7Q%2WjYmN6blj0qJsc^n&)&BjpLSkj1}s$+@9s* zVYLGfMcn1l%&bY=o^|eUR%|&wB=)`y%LYsG*IY{811;OTmw zUF*4WZQ2vfPrSSRxC%JhB|9>Lr}I=e5lO(fxjcdrsr8%wMv9pU;RsV#Y`{#VRRU{_ zUnE(-h-hJ$hza8rQZW5imrd1iTYwf)Wf9JfBlRkuIz4gWQ~K_Ai~J;Ov*b^5jDYR@ zv1!q|$!0BXV{cu@eBtKl3b$w-a#q#Si#kOsA#KMz<;VOJM%kTTF{X+s;hY{}9TseL zBILmq=Ttf?Y8MOD`!2q~&R(W-YpOY~G94v1+4qh%7kn;dv(xqpi0-S$#n8_3Q4RckePWJe^JhzS#A}xoOaN`%2Fr ztEHmWc9weo*?q0JCp#sF$tw%bAcuLe>aZI#a&My`hXW@z0yj{~FMcd6)ZJAd#GWZ5 zc`$OF@dX#h_9JZo&czB)O9eZ}5@C?v?|MM2*e8-Bp! z#i0Ua0#8YXsfC3H4K2}P4zJ)fft3Mm-A&CR{>41uGon;w*2wq({oIywu%$_I-_AhX za#bb5(Wp&DT{G|S3>~xg@oe?j)*Yc#kBXjDR24I^FIX@xjXJsS^S2QD_Qc8?7E~rS zmXcuXAC^_hu&^$5+*Y@42%z4Ku^$HRkO|7%hW6P231JB zTVtzLUN53)fW;SoWQ|XH8pGMI_{ef(+NgV`^ejOuDepZBWk3FSe{}!*BB`gI1#r}> zS?tsv>`I!I^*e`fGS-SXXtSGjG6o+pcRJs^qia5ew6Xn9HeuAndA}!uV6mkC3@wxCmdZ9eWYdab(XFO+CfF>5n1Xi}k1|Rba># zd*eF)?Gqh;`t0BLMMtSl+hc$*zggN(lFKAi9YcDX6+lYBLy4@Qr{bjEx6BP6)_|X~ zER(?*o-LciriJr+>^6bt9)TAL8;52jsms6E^EptKkF#-;W5D?Wr`^8MVuZ~yjb~;1 zVjy`hasKSf0H=h<)!wN##K(oZME3{R+enUI$iKNWt^=|R?aLr5*O;#LNwqrtl;sit~z!Jk6<`tYH-zk~F(frOvUt z5zAy2wpmzGVRFo)5}xIG$)%m*lbM_Gm13=(Hcq{`Awfn_(gQ3G>_%~KlkwR<9H{k* z=Nr*;4pz!ynehzE4xNV6k_;AZKKJDVk>|50k!2cw!BSkVv~qWzwc>_^+ zGj)aCD;lrX5XH{XS7!=ZupTCM(hcZwX9oJ4-otMV6C;^5!1K8bmy_kj$*pVl&V5Rq zHD$a#q4*Bw9SR*mo}v^ls3Q)RQ&~-1t?7fdu&0^vmZoSJwp`r7s{Yn96`y`lhU`pL zEv`6s34>wjTL;?6)52-U$9dj}lQ6Nb3ion+?&3_0Ify30*{=$8x^j-G zbq?wHQnMij+*^vO0k0RroH;bw!?S?8Y9Q1K?~o*onxEJVJ4$2K{RZyA*wpk z!`!xV(Ng98G(M(sz*H)eW_j%)=15d$z{YOt1tkLm{%0l`P<&Di!wWGN7$iqZ?-!Om zQF8J|!sW6D(#B1q_c3P?c$SX36D-vqr^u41aknC9I+^HAKAc)WS?{-kp}maAGRl2K zLs*JhfVusML7#(SpUpj=oxk;dP09U!=fLBx&44P%ljAf|RM&v78BbH!p{=l7RU-Dd z3aDPhs-(0@+#f`He~)G=UQ9%0dJ^A8*mxnw^b2N`g?*Xl{OqefkGk~2ZPM(f!xfVG zSLv+MfD}uaI&+P3CG~ z^7Fh`(0uO>sm7;gQ1T&#?cwAV>*D#SQ;+lqNm#6_S?P{_=h<A5i-6NX0l6dB0gB^)}ij4)So+rCNGk7pwMKK`tx5qr^(Dfx7h^0r@$u zzgWt9P9c>r(v1cv>ym+&c%Ixj_Z`MBUO$$1XS`3oZGWapMSX!9bq*(U9&6)K1~mYw z%xue!6&01Sr;b@#|KK1~1g)AEUEhgwi4pEVS@4R2q_GNW=@iR&)+A=Pl}s zbNe%F$cf(2`G*2J%EC{dJ$xRj=hZHepwT3>N-&BzC%p6C#GI}xeu4t}!+hvfbgR3p z+Dqz|8Kw3a@2^U3&>xh%661WUk}LyqB%5ErI=)u&r|Vt3R)U<+^!@^g{`?g=TK^uY zIE5L37NUY$?pN@~QOjLE}LwkWFeT5^Gm*aeXWLn`q}H}7|4aI8Gsnx|U6 z*`4mtPm%X<8#Tx~sjgn8^<7nZhsuS8qSg%=U_k zO6)Vpj8%0hsPlqQpK@`WdsM%jJ?$|puE{IMH|RkI`7-+@;Y(t_C#D|PB5>DP?=IfF z-FoK9rXL~BbG5($t(ZDJe`Zbt^))24 zFc1)C!0~xK!{_zq71f}&q zUbEDD4zdD{j`>^Wk1rE|y{!1ZFB@1}J~Xn>Gq!iIwgTOVq5?w&--dFq)3dTS0@B$( zp#L!~spAtq?ulp$Jm9>rdZ2LMQ;ff#k>7{f0OfYJwD_mdzw2A`J~g!k0JH&qzn?nx z_apA~KZ*md``-or&bG0$wgK5Wn1by8sb16{Yy+fE|9EhmAs_%Im==G018}E*Je+^M z@pB9O_%7~0-h_Yv{m;99*Wjiy6M<0@ZlC}kgPK}{``3>}0*8IN0jBTh0JzwVx*-HB z8J5eR+WcELf7cH#0g5g4Mz=50%KsLAP4V*W4 z1JB6R!ouFc#p34jf!hJq_1CHcc)%yVH&zzB(U7?bW^LtgBMRK4em9^@zpt6z$O6|{ z`wc7$VDs_qIs@lHz4*1xEUllLf($H7ZEh?dcwL2W;Qe_|^iBQ-&U$qd>yJA{ZeoF( zv*8BTAD@BWgaR+t*-a>GYYY1u)dgNGR<~cP-_gO;;zks>C9Q5l8Qtc!WMI)=bQ|w> z*98ae-3I)_-)^iiIFQNx7UkXUaNt1OTYH*E_s{_vdQzcQ^h|*73d9 zGJgMQs{yd9H*hR49hiL|di+{@e|3QZfAoZZZszzA+(<6~&36|9f*(j}7{S0oFMks( z40J(1hnt-m=_vr~>Onw?%mN0V^ZH#l=#Sp==X~A@D7FzGiwOc!*6*Kn{QZRb{U%>l z-wb5%*OcXty5Y=XxfTO5{9_>P`Mv?*@255BH}Oh9*KGgu!UwSTvV8%_TLeGSjU#WR z3+maM8r)=8V+${4HAo1EOF%s|e-|A1t2zBvx{#iQ#m@`9U{PX$10XSheMY=sSnI4? zu_FITF2wb$3@t!*)9ETPo3#g3) z;6LC|6;^$#2r-a@vfVG#q-I=#kqkIKGEn~S$Cv#5gaT3gJ~gfT1dQ!NrDu3ofmv5Bh~cGs+NA zr~x)Kzy=>mR*v7wmN5Exf$;~{ndty18UO{4QI_djp^}c4Hb2J_^B-gg0Zlp&U_cHq z*Ql7k75fzAB& z3moA4eHy~$=OvyWf48d%lr{t~0JuW%kZwmi+MC>L+o)vyyhWg$;k^H&LPk)23;**{ zD{FE0fA0ZN0ha+UH3Gq{*guc;1=$H%+x$W)J`v6{`hZeU01@CRlPAe<<0Y&NLC!xf zJ_Yi}gnIy+11LUtBPm3FD<3#oOG(ep_&*yQe{9`l2m+jJfYzM}2wGY&U&*1qmHm&C zhJNI763#MycN9pV+~B>24dd@|Z&1iCG79mVpT-R}a0kaNUA2m}so~G{JlW1hoAlj= z|C5@+u-%GP)iZSf*3xh2NJae0NAUnWAz(&uH-Y5*SNLD_rKS&!3%CJORsyC3Z%tnC z-p)65F!{MwGW6GseSl(116rX5^AIHAUtkC{CoYYS^TrTeM*25pquaME;!xL=6C79B#~Gbbp|WD*}d);9jHl>R;i1 z(F%xgJ3~AN^nwo97F;(1LBHnz_asK}TZevBd`sVZemo$n=m8d|z{3eM{MYb5=gtL9 zjlmAt{*m2jv>#~(gz5mGd+_GVKj}8M(D&K-pXA!{j?+Ezk zd~%*J3rB$c46rF3So4^hbvxhc`z+MY!5wS1C`AD982F_J13MMm4z_Xm`xL+*HQgF- zHhd2#vIjZ@1aQ6dKc1I`w~_xcA^2SfKMGhRim}}T{7)I<#}L{qyG?+!se^+B=tfU* zz(au}MumVVBmU6~AHTm1uc#_|BRkf6=J^1i5fdQ208bUm`+tM|NBDq#t$I8bHUkIy8z=vfF@m>U0L)Sn}*dc+6Fcn!$-VI~maH)N=M zU&S}LQNjw_>0}kq1iYmE(Lb4xe?x*QNMBCR0_5=L@_|01fH^skpCM0 zXYTUrl>vR&p3y8oupSR1^* zz<&O(q}-4WjH^4shyb;K0%k42=Q6%H{gw=CptlgS(=#*$-1pb^7>u$0{CzHi49FkB z$JE(gza!>95`J!uqL`xX6rd8r4}R?Qc>Vqj{nzDZLTp+k1I!=?1RZ#{$`|o(B>Z~l zo+HCS+6}nW1Q2K7DNb9|??{jUhGTX%))qIc2xOhT@0$gJ?iCQmkHNxtH|}>t$XMIi znA{e5!y*)VaDbMC0DFM1*fOR4wvhjZ-;MF%F_0cC12|M2FdYHDnv$OR`=Wl&fvu#) zRKHJ6OaSu0&7)QN+p=!<;`odcO*~-a83iP{;GxJ~^{++z+Ld8n+vCUrHTjX*Kb9}K9U3K%4TcO5(* z{}unn%?!EO^lu7)_1I2e76ZJ!Wg7iAQhwgvPH(u7r~|F}5#U1L^K@LZx6>^g?fz8J z&*8pHSoc-{_$Ux*;QqP4bSwOiF{F&%|8;iVaZw#xc#&em9*vs#>;*eY%tKT}L;+C| zP*7u8VUZ1Gby-0S7`w(o6vRY{K@tR8)MtqXH9-(8sCg*xVu>Z1h%Jg8CHlU5QQWiK zxp&t6{r2DQ%zSg&oH;Yspx4fnlaZaVGaw5K9E%B1&++e@95N!JbYW<-(?z^qYT2d8 zt2I4ZFzY!eR@3;bI)_LrJ6mnt7(J;I4$^nYzPh4+ZqfgFWMW{-#B^*#k6|T|rSMN|Lk+ zRohDezoKY31QU@S`oXIX(XZgH=0T$Xt@<=of~ z(%2JY_bq>h^-REeQdRsbNda3>l1gP~@zVM1z*UpMdVOTC{fKKA@U>a^z6QN6RO7D= zV)eDSU)w%59_AVarKg%%BiS5D(md%m)v+uGPch($n$)8-4s?%*;L7h9m|AP{{avLs z;W^zU$b~MfE(2}&9rLfD*z7-i0;lSOf&VMT0<81 zLilM8i)lkf>vKsCRl^|HYJ6e$E5I56>!T@P;U8?gihEBLF-LszRGQkucg+B29IT|+J!|n2da(_4ciKKt^n?%H)7x#R%*0_IpA_gr(icjaTu#TA$7?H?5^T)Jc zM2b9UCDkDNCQ1@S^5rrjpUnNQ(xB!=_)~ZFs3{jX^T2bn(*(y}Fwe)xw>~0<^Z?DF zEc2|OULgHVgO*J1?N^&T7kmI8o(M5?@QYjWh!BF0^r}8A@$TS*Rc(ySq8%B0c^fve zM>zWs%kk!*vT*W$cBA;G1yf-P3APL(c~5OjzA9wI^EWf@5h7gsnbTt!1ryv z@)F*RqUki!Ig6>)9!`yI<}DE!nJt@ zpnX9jp}jN#DoPXb)3X%-T@0v&qxGD1E7Z%Wz%v^@%pU0mtuQ1mU_-yX;$M-^h+}uf zm4E5+7hW&jyhd=n5Q06U(Jylaj}VUttwE~})6UW;2s%D3DM@H{$%gx+n)=fk9zmmK zXahrWv8qCj^kmc}4`yN;q*{X<1X^VDdzs9ZAr73~pY&p4--)A;OdGJppb7QU6eUo( z8_S*NrVPnNI(7p(PvgmktqQpAI3i=U{+zRj`t~AtP6W?%-*OPw+gOoL+FOKAE}RpMVM{HH9i+|6qLLw5@fV5MTQL+oI*Vk*_tLkh6WWN3S}BdrdCgKAi| zP6RXRXC+MbE$ytsi#wM=v*K`Y*Os6z$zh{EC>&fXHtAaI)z>X%tjG6nfO&h)Otv7c!uFoq_p~K-#WZ zC%0pa(Xb*q^QIQoOe^xygzKUhTGXkny=z|}w#Ckr4zj6@5+Wnj?Ht(kjz8vYfuKfR z)5cnCNME!Yp?ok@&$3rmT=O!r~*+Yn!y*chFtMViR%nKYt8mL zYwS*-{?h>-nkJrKwpW6E6V}o2Bi{3pwuiFRo)#Kxl6Qk>x*t5y$6N^vEh!FqKHw;L zc^`UAcgpkK`9#Q!e~#sqI!lnKAmC%JuUs_)drP>yGA`Ue|E=bd} zL(7y)nt;gz)uzlfOLTJ0u zPrH^2mYwtXsHV1y11qb+3SG(D>-nJNGQf3V#q+W-+*OFK)PV9*xzK%pu4gn$Z9Q`M z@hK>w&{o~uh!d6S+IL*wGW+r_aW_+Qfb9=#I&fQ*p5?uwxR4??!!`}uA82viQssmt zrDdkBxm@7PwD9@yr!OudNB9-iLvw_(JT|g#2$~z%@0cvo7~T)8j%zU!pV3-@+IAie+rv;eZdPzl^E0u66?bfKM>(D^29^+yx53v|G*246zC zc013DFOA%>uJazD4dx&$htg8_iW0I&h+G+UY=C=Nbu6TCT1df9qdkqOejpnKyN}24T*ror5)5j+LBF2x6q3S>M>dabD+itYXRsraGR7Czx57;;VcW?$(caHG1#m z(*aiiZHGz^UB07zxS<&gr=00l+znpk0$dhVK1)|_>>*lBSP-klAw%tYtmHkFY6H?Y z>bDZy%#$E(KKKmS1|D3M9`nH1bwlwkjOqZGr}^8(AO&Dwyc3}f6qJRTPjpN6So9F+pJGkv;fgj` z0bRhw6FLU$mm^sVTH>?ZbsPqM4$M%MIS^tFJbOd#)O2-xe{g;gW>0-ebf_5;q`Cj? z=X=eDA+H$Q%5+g170#5_$01EpeVpVm`Hj zHg3Y7_7AlBmKRxe=*fq*F)yHx^-u$)`nWiSSHPQNW@++k-ftS)4%%J<3DjJ#f5$5U z?+q}V!HTp~MSsIovw=7?%c5w2)Yh}XxVGQMYj+kL-+>h_tFp=#Ib0(A z-aHk;a{K`vb0VFv2Ai=@?FT7ue+`eADM4sMbBi#rG`8FEg}M$fxB~1)=)IABd1guy z8%JbB%|5FxiRcLTG{V%ey62lGN?N;0AJ|Rq1k%unWwP>39xC8hbioW7?)_S|SnD!4 zsnjz7aiI;O4s{@@kIfVzJ%{;g`Yh;)In>6I&<6H#b*eK{EHU`Jp2LjbV2xfABsL-` z%;wC}1&PBDg_0dj*ZwIyJS(455GFmZy4gef7uSQRmZqY9urps&#bY=O4{6`EXxdEp zu6fXC>Hz+Bn+GHy-+Kp0kMXs86w1sUDBH6v9U|e zq{b@Md<$-DB0Q^j;|2rOp6!!Tez^j)G;iN*Qejr=p0ztHn2i>TrN8mup@=1D0{f7Bcjxd zB9YbnXOWrMC!RxDj(Y$2Bbu_`AkVn+Ty zx2<8qPE|~}K{3Awd4GMfAb!v-`0Mpm{PnDJ{4!+yb-ggVcxi8U^b)@ad2cu0db!7V zNpF{ZnO}yyx69jk;$%~JyFIWos#0^Wamny^86&&i*aNz+gK_hAavnwZv>yzcWzs3=_Fp(ttiZ&K^^Uxo literal 0 HcmV?d00001 diff --git a/plugin/jcapture/license.txt b/plugin/jcapture/license.txt new file mode 100644 index 000000000..0c73c0553 --- /dev/null +++ b/plugin/jcapture/license.txt @@ -0,0 +1 @@ +GPL v 2 \ No newline at end of file diff --git a/plugin/jcapture/plugin.php b/plugin/jcapture/plugin.php new file mode 100644 index 000000000..cd3975d71 --- /dev/null +++ b/plugin/jcapture/plugin.php @@ -0,0 +1,12 @@ + 'Jcapture Screencast', + 'comment' => "Screencast widget", + 'version' => '1.0', + 'author' => 'Julio Montoya', +]; diff --git a/plugin/jcapture/plugin_applet.php b/plugin/jcapture/plugin_applet.php new file mode 100644 index 000000000..a9fc036ab --- /dev/null +++ b/plugin/jcapture/plugin_applet.php @@ -0,0 +1,84 @@ +getInstalledPlugins(); +$capturePluginInstalled = in_array('jcapture', $pluginList); +if (!$capturePluginInstalled) { + exit; +} + +$capturePath = api_get_path(WEB_PLUGIN_PATH).'jcapture/'; +$hostName = api_get_path(WEB_PATH); + +define('DOKU_BASE', '/tmp'); +function getSecurityToken() +{ +} + +//close sesseion +session_write_close(); + +header('Content-Type: text/html; charset=utf-8'); +$imageFormat = "PNG"; +$cookies = null; +foreach (array_keys($_COOKIE) as $cookieName) { + $cookies .= bin2hex($cookieName)."=".bin2hex($_COOKIE[$cookieName]).";"; +} + +$pageName = 'file'; +$edid = '1'; +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/jcapture/script.js b/plugin/jcapture/script.js new file mode 100644 index 000000000..b5f0234c5 --- /dev/null +++ b/plugin/jcapture/script.js @@ -0,0 +1,19 @@ +function addBtnActionJCapture($btn, props, edid) { + $btn.click(function() { + var jCaptureApplet = document.getElementById("jCaptureApplet"); + if (jCaptureApplet==null) { + var oNewDiv = document.createElement("div"); + oNewDiv.id="jCaptureAppletDiv"; + //oNewDiv.style.display='none'; + document.body.appendChild(oNewDiv); + jQuery("#jCaptureAppletDiv").load(DOKU_BASE+"lib/plugins/jcapture/applet.php?edid="+edid+"&pageName="+document.forms['dw__editform'].elements['id'].value); + } else { + jCaptureApplet.showCaptureFrame(); + } + return false; + }); + + return true; +} + + diff --git a/plugin/jcapture/src/META-INF/services/com.hammurapi.jcapture.VideoEncoder b/plugin/jcapture/src/META-INF/services/com.hammurapi.jcapture.VideoEncoder new file mode 100644 index 000000000..1fbadfcc9 --- /dev/null +++ b/plugin/jcapture/src/META-INF/services/com.hammurapi.jcapture.VideoEncoder @@ -0,0 +1 @@ +com.hammurapi.jcapture.SwfEncoder \ No newline at end of file diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/AWTUtilitiesTranslucener.java b/plugin/jcapture/src/com/hammurapi/jcapture/AWTUtilitiesTranslucener.java new file mode 100644 index 000000000..e4ecc2334 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/AWTUtilitiesTranslucener.java @@ -0,0 +1,17 @@ +package com.hammurapi.jcapture; + +import java.awt.Frame; + +import com.sun.awt.AWTUtilities; +import com.sun.awt.AWTUtilities.Translucency; + +public class AWTUtilitiesTranslucener extends Translucener { + + @Override + protected void makeTranslucent(Frame frame) { + if (AWTUtilities.isTranslucencySupported(Translucency.TRANSLUCENT)) { + AWTUtilities.setWindowOpacity(frame, 0.7f); + } + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/AbstractCaptureApplet.java b/plugin/jcapture/src/com/hammurapi/jcapture/AbstractCaptureApplet.java new file mode 100644 index 000000000..54db002f4 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/AbstractCaptureApplet.java @@ -0,0 +1,275 @@ +package com.hammurapi.jcapture; + +import java.awt.Component; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProxySelector; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import javax.swing.JApplet; +import javax.swing.JOptionPane; +import javax.swing.ProgressMonitorInputStream; +import javax.swing.SwingUtilities; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.net.URLCodec; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.mime.content.InputStreamBody; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.ProxySelectorRoutePlanner; + +/** + * Base class for capture applets. + * @author Pavel + * + */ +public abstract class AbstractCaptureApplet extends JApplet { + + private static final String OUTPUT_DIR_PARAMETER = "outputDir"; + + private CaptureFrame captureFrame; + + @Override + public void stop() { + if (captureFrame!=null) { + captureFrame.dispose(); + captureFrame = null; + } + backgroundProcessor.shutdown(); + synchronized (closeables) { + Iterator cit = closeables.iterator(); + while (cit.hasNext()) { + try { + cit.next().close(); + } catch (Exception e) { + e.printStackTrace(); + } + cit.remove(); + } + } + super.stop(); + } + + /** + * Adds closeable to close in applet stop() method. + * @param closeable + */ + public void addCloseable(Closeable closeable) { + synchronized (closeables) { + closeables.add(closeable); + } + } + + private Collection closeables = new ArrayList(); + + public void showCaptureFrame() { + System.out.println("Showing capture frame"); + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + try { + if (captureFrame==null) { + createCaptureFrame(); + } + captureFrame.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + }); + } + + private ExecutorService backgroundProcessor; + + @Override + public void start() { + super.start(); + + ThreadFactory threadFactory = new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + Thread th=new Thread(r, "Background processor"); + th.setPriority(Thread.NORM_PRIORITY); + return th; + } + }; + backgroundProcessor = Executors.newSingleThreadExecutor(threadFactory); + + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + createCaptureFrame(); + } + + }); + + try { + // Proxy configuration - requires java.net.NetPermission getProxySelector + proxySelector = ProxySelector.getDefault(); + } catch (Exception e) { + System.err.println("Can't obtain proxy information: "+e); + e.printStackTrace(); + } + } + + public ExecutorService getBackgroundProcessor() { + return backgroundProcessor; + } + + protected void createCaptureFrame() { + try { + captureFrame = new CaptureFrame(this); + captureFrame.setVisible(true); + } catch (Exception e) { + JOptionPane.showMessageDialog( + null, + "Error: "+e, + "Cannot create capture window", + JOptionPane.ERROR_MESSAGE); + e.printStackTrace(); + } + } + + public static String formatByteSize(long bytes) { + if (bytes<1024) { + return bytes + "bytes"; + } + if (bytes<1024*1024) { + return MessageFormat.format("{0,number,0.0} Kb", new Object[] {(double) bytes/1024.0}); + } + if (bytes<1024*1024*1024) { + return MessageFormat.format("{0,number,0.00} Mb", new Object[] {(double) bytes/(double) (1024.0*1024.0)}); + } + return MessageFormat.format("{0,number,0.00} Gb", new Object[] {(double) bytes/(double) (1024.0*1024.0*1024.0)}); + } + + + protected File preferencesFile = new File(System.getProperty("user.home")+File.separator+"."+getClass().getName()+".properties"); + + public Properties loadConfig() { + try { + if (preferencesFile.isFile()) { + InputStream configStream = new FileInputStream(preferencesFile); + Properties ret = new Properties(); + ret.load(configStream); + configStream.close(); + return ret; + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public void storeConfig(Properties properties) { + try { + FileOutputStream out = new FileOutputStream(preferencesFile); + properties.store(out, "Config"); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected String getCookies() throws DecoderException { + String cookiesStr = getParameter("cookies"); + if (cookiesStr==null) { + return null; + } + + StringBuilder ret = new StringBuilder(); + StringTokenizer st = new StringTokenizer(cookiesStr, ";"); + while (st.hasMoreTokens()) { + String tok = st.nextToken(); + int idx = tok.indexOf("="); + ret.append(hex2urlEncoded(tok.substring(0, idx))); + ret.append("="); + ret.append(hex2urlEncoded(tok.substring(idx+1))); + if (st.hasMoreElements()) { + ret.append(";"); + } + } + + return ret.toString(); + } + + private String hex2urlEncoded(String hexStr) throws DecoderException { + return new String(URLCodec.encodeUrl(null, Hex.decodeHex(hexStr.toCharArray()))); + } + + protected ProxySelector proxySelector; + + /** + * Posts capture/recording to the web site. + * @param parentComponent Parent component for the progress bar. + * @param content Content - file or byte array. + * @param fileName File name. + * @param mimeType Mime type. + * @return + * @throws Exception + */ + public HttpResponse post( + Component parentComponent, + final InputStream content, + final long contentLength, + String fileName, + String mimeType) throws Exception { + + + System.out.println("jCapture applet, build @@@time@@@"); + + /** + * Debugging - save to file. + */ + if (getParameter(OUTPUT_DIR_PARAMETER)!=null) { + OutputStream out = new FileOutputStream(new File(getParameter(OUTPUT_DIR_PARAMETER)+File.separator+fileName)); + byte[] buf=new byte[4096]; + int l; + while ((l=content.read(buf))!=-1) { + out.write(buf, 0, l); + } + out.close(); + content.close(); + return null; + } + + ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(parentComponent, "Uploading "+ fileName + " ("+formatByteSize(contentLength)+")", content); + InputStreamBody bin = new InputStreamBody(pmis, mimeType, bodyName(fileName)) { + + @Override + public long getContentLength() { + return contentLength; + } + }; + + DefaultHttpClient httpClient = new DefaultHttpClient(); + if (proxySelector!=null) { + ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner( + httpClient.getConnectionManager().getSchemeRegistry(), + proxySelector); + httpClient.setRoutePlanner(routePlanner); + } + return httpClient.execute(createRequest(fileName, bin)); + } + + protected abstract HttpUriRequest createRequest(String fileName, InputStreamBody bin) throws Exception; + + protected abstract String bodyName(String fileName); +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ButtonManager.java b/plugin/jcapture/src/com/hammurapi/jcapture/ButtonManager.java new file mode 100644 index 000000000..eb9bbfc9a --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ButtonManager.java @@ -0,0 +1,85 @@ +package com.hammurapi.jcapture; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.zip.DataFormatException; + +import com.flagstone.transform.DefineTag; +import com.flagstone.transform.Movie; +import com.flagstone.transform.MovieTag; +import com.flagstone.transform.Place2; +import com.flagstone.transform.datatype.CoordTransform; + + +public class ButtonManager { + + private final Map buttons; + private final List definitions; + + public ButtonManager() { + buttons = new HashMap(); + definitions = new ArrayList(); + } + + public void loadLibrary(URL libUrl) throws IOException, DataFormatException { + Movie movie = new Movie(); + movie.decodeFromUrl(libUrl); + findDefinitions(movie, definitions); + findButtons(movie, buttons); + } + + public int maxIdentifier() { + int identifier = 0; + DefineTag object; + for (Iteratoriter = definitions.iterator(); iter.hasNext();) { + object = iter.next(); + if (object.getIdentifier() > identifier) { + identifier = object.getIdentifier(); + } + } + return identifier; + } + + public List getDefinitions() { + List list = new ArrayList(definitions.size()); + for (Iteratoriter = definitions.iterator(); iter.hasNext();) { + list.add((DefineTag) iter.next().copy()); + } + return list; + } + + public Place2 getButton(final String name, final int layer, final int xpos, final int ypos) { + Place2 place = (Place2)buttons.get(name).copy(); + place.setLayer(layer); + place.setTransform(new CoordTransform(1, 1, 0, 0, xpos, ypos)); + return place; + } + + private void findDefinitions(final Movie movie, final List list) { + MovieTag object; + for (Iterator iter = movie.getObjects().iterator(); iter.hasNext();) { + object = iter.next(); + if (object instanceof DefineTag) { + list.add((DefineTag)object); + } + } + } + + private void findButtons(final Movie movie, final Map list) { + MovieTag object; + Place2 place; + for (Iteratoriter = movie.getObjects().iterator(); iter.hasNext();) { + object = iter.next(); + if (object instanceof Place2) { + place = (Place2)object; + if (place.getName() != null) { + list.put(place.getName(), place); + } + } + } + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/CaptureConfig.java b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureConfig.java new file mode 100644 index 000000000..fbd6a0ab8 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureConfig.java @@ -0,0 +1,370 @@ +package com.hammurapi.jcapture; + +import java.awt.AWTException; +import java.awt.Component; +import java.awt.MouseInfo; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import javax.sound.sampled.AudioFormat; + +public class CaptureConfig implements VideoEncoder.Config { + + private static final String MP3_COMMAND_PROPERTY = "mp3command"; + private static final String TOOL_BAR_PROPERTY = "toolBar"; + private static final String SPEED_SCALE_PROPERTY = "speedScale"; + private static final String SOUND_PROPERTY = "sound"; + private static final String SCREEN_SCALE_PROPERTY = "screenScale"; + private static final String REMOVE_INACTIVITY_PROPERTY = "removeInactivity"; + private static final String PLAY_PROPERTY = "play"; + private static final String MOUSE_PROPERTY = "mouse"; + private static final String MIXER_NAME_PROPERTY = "mixerName"; + private static final String LOOP_PROPERTY = "loop"; + private static final String INACTIVITY_INTERVAL_PROPERTY = "inactivityInterval"; + private static final String IMAGE_FORMAT_PROPERTY = "imageFormat"; + private static final String FRAMES_PER_SECOND_PROPERTY = "framesPerSecond"; + private static final String BORDER_PROPERTY = "border"; + private static final String RECORDING_RECTANGLE_PROPERTY = "recordingRectangle"; + private static final String ENCODER_NAME_PROPERTY = "encoderName"; + private static final String AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY = "audioFormat.sampleSize"; + private static final String AUDIO_FORMAT_SAMPLE_RATE_PROPERTY = "audioFormat.sampleRate"; + private static final String AUDIO_FORMAT_CHANNELS_PROPERTY = "audioFormat.channels"; + private AudioFormat audioFormat = new AudioFormat(22050.0F, 16, 1, true, false);; + private String mixerName; + private float framesPerSecond = 10.0f; + private double screenScale = 1.0; + private float speedScale = 1.0f; + private boolean removeInactivity; + private double inactivityInterval = 0.7; + private Component parentComponent; + private Rectangle recordingRectangle; + private boolean border = true; + private boolean toolBar = true; + private Robot robot; + private String imageFormat = "PNG"; + private boolean sound = true; + private boolean mouse = true; + private boolean loop = true; + private boolean play = false; + private VideoEncoder encoder; + private int grabRange = 3; + private ExecutorService backgroundProcessor; + private String mp3command; + + public String getMp3command() { + return mp3command; + } + + public void setMp3command(String mp3command) { + this.mp3command = mp3command; + } + + public int getGrabRange() { + return grabRange; + } + + public ExecutorService getBackgroundProcessor() { + return backgroundProcessor; + } + + public void setBackgroundProcessor(ExecutorService backgroundProcessor) { + this.backgroundProcessor = backgroundProcessor; + } + + public void setGrabRange(int grabRange) { + this.grabRange = grabRange; + } + + public VideoEncoder getEncoder() { + return encoder; + } + + public void setEncoder(VideoEncoder encoder) { + this.encoder = encoder; + } + + public boolean isLoop() { + return loop; + } + + public void setLoop(boolean loop) { + this.loop = loop; + } + + public boolean isPlay() { + return play; + } + + public void setPlay(boolean play) { + this.play = play; + } + + public boolean isSound() { + return sound; + } + + public void setSound(boolean sound) { + this.sound = sound; + } + + public boolean isMouse() { + return mouse; + } + + public void setMouse(boolean mouse) { + this.mouse = mouse; + } + + public String getImageFormat() { + return imageFormat; + } + + public void setImageFormat(String imageFormat) { + this.imageFormat = imageFormat; + } + + public CaptureConfig() throws AWTException { + robot = new Robot(); + + ServiceLoader sl = ServiceLoader.load(VideoEncoder.class); + List accumulator = new ArrayList(); + Iterator vit = sl.iterator(); + while (vit.hasNext()) { + accumulator.add(vit.next()); + } + + Collections.sort(accumulator, new Comparator() { + + @Override + public int compare(VideoEncoder o1, VideoEncoder o2) { + return o1.toString().compareTo(o2.toString()); + } + + }); + + encoders = Collections.unmodifiableList(accumulator); + if (encoder==null && !encoders.isEmpty()) { + encoder = encoders.get(0); + } + + } + + /** + * Submits screenshot for processing in a background thread. + * @param task + * @return + */ + public Future submit(ScreenShot task) { + return backgroundProcessor.submit(task); + } + + public Robot getRobot() { + return robot; + } + + public ScreenShot createScreenShot(ScreenShot prev, FileChannel imageChannel) throws IOException { + BufferedImage image = robot.createScreenCapture(recordingRectangle); + Point mouseLocation = MouseInfo.getPointerInfo().getLocation(); + if (mouse && recordingRectangle.contains(mouseLocation)) { + mouseLocation.move(mouseLocation.x-recordingRectangle.x, mouseLocation.y-recordingRectangle.y); + } else { + mouseLocation = null; + } + return new ScreenShot( + image, + mouseLocation, + prev, + System.currentTimeMillis(), + grabRange, + isTransparencySupported(), + border, + getScreenScale(), + imageChannel, + getImageFormat()); + } + + public boolean isTransparencySupported() { + return !"jpeg".equalsIgnoreCase(getImageFormat()) + && !"jpg".equalsIgnoreCase(getImageFormat()); + } + + public boolean isToolBar() { + return toolBar; + } + public void setToolBar(boolean toolBar) { + this.toolBar = toolBar; + } + public boolean isBorder() { + return border; + } + public void setBorder(boolean border) { + this.border = border; + } + public Rectangle getRecordingRectangle() { + return recordingRectangle; + } + public Properties setRecordingRectangle(Rectangle recordingRectangle) { + Rectangle oldValue = this.recordingRectangle; + this.recordingRectangle = recordingRectangle; + if (this.recordingRectangle!=null && !this.recordingRectangle.equals(oldValue)) { + return store(); + } + return null; + } + public AudioFormat getAudioFormat() { + return audioFormat; + } + public void setAudioFormat(AudioFormat audioFormat) { + this.audioFormat = audioFormat; + } + public String getMixerName() { + return mixerName; + } + public void setMixerName(String mixerName) { + this.mixerName = mixerName; + } + public float getFramesPerSecond() { + return framesPerSecond; + } + public void setFramesPerSecond(float framesPerSecond) { + this.framesPerSecond = framesPerSecond; + } + public double getScreenScale() { + return screenScale; + } + public void setScreenScale(double screenScale) { + this.screenScale = screenScale; + } + public float getSpeedScale() { + return speedScale; + } + public void setSpeedScale(float speedScale) { + this.speedScale = speedScale; + } + public boolean isRemoveInactivity() { + return removeInactivity; + } + public void setRemoveInactivity(boolean removeInactivity) { + this.removeInactivity = removeInactivity; + } + public double getInactivityInterval() { + return inactivityInterval; + } + public void setInactivityInterval(double inactivityInterval) { + this.inactivityInterval = inactivityInterval; + } + public Component getParentComponent() { + return parentComponent; + } + public void setParentComponent(Component parentComponent) { + this.parentComponent = parentComponent; + } + + void load(Properties properties) { + if (properties!=null) { + try { + if (properties.containsKey(AUDIO_FORMAT_CHANNELS_PROPERTY)) { + audioFormat = new AudioFormat( + Float.parseFloat(properties.getProperty(AUDIO_FORMAT_SAMPLE_RATE_PROPERTY, String.valueOf(audioFormat.getSampleRate()))), + Integer.parseInt(properties.getProperty(AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY, String.valueOf(audioFormat.getSampleSizeInBits()))), + Integer.parseInt(properties.getProperty(AUDIO_FORMAT_CHANNELS_PROPERTY, String.valueOf(audioFormat.getChannels()))), + true, false); + } + + border=Boolean.parseBoolean(properties.getProperty(BORDER_PROPERTY, String.valueOf(border))); + framesPerSecond=Float.parseFloat(properties.getProperty(FRAMES_PER_SECOND_PROPERTY, String.valueOf(framesPerSecond))); + imageFormat=properties.getProperty(IMAGE_FORMAT_PROPERTY, String.valueOf(imageFormat)); + inactivityInterval=Double.parseDouble(properties.getProperty(INACTIVITY_INTERVAL_PROPERTY, String.valueOf(inactivityInterval))); + loop=Boolean.parseBoolean(properties.getProperty(LOOP_PROPERTY, String.valueOf(loop))); + mixerName=properties.getProperty(MIXER_NAME_PROPERTY, String.valueOf(mixerName)); + mouse=Boolean.parseBoolean(properties.getProperty(MOUSE_PROPERTY, String.valueOf(mouse))); + play=Boolean.parseBoolean(properties.getProperty(PLAY_PROPERTY, String.valueOf(play))); + removeInactivity=Boolean.parseBoolean(properties.getProperty(REMOVE_INACTIVITY_PROPERTY, String.valueOf(removeInactivity))); + screenScale=Double.parseDouble(properties.getProperty(SCREEN_SCALE_PROPERTY, String.valueOf(screenScale))); + sound=Boolean.parseBoolean(properties.getProperty(SOUND_PROPERTY, String.valueOf(sound))); + speedScale=Float.parseFloat(properties.getProperty(SPEED_SCALE_PROPERTY, String.valueOf(speedScale))); + toolBar=Boolean.parseBoolean(properties.getProperty(TOOL_BAR_PROPERTY, String.valueOf(toolBar))); + mp3command=properties.getProperty(MP3_COMMAND_PROPERTY); + encoder = null; + String encoderName = properties.getProperty(ENCODER_NAME_PROPERTY); + if (encoderName!=null) { + for (VideoEncoder candidate: getEncoders()) { + if (encoderName.equals(candidate.toString())) { + encoder = candidate; + break; + } + } + } + if (encoder==null && !getEncoders().isEmpty()) { + encoder = getEncoders().get(0); + } + + String rr = properties.getProperty(RECORDING_RECTANGLE_PROPERTY); + if (rr!=null && rr.trim().length()>0) { + String[] dims = rr.split(";"); + recordingRectangle = new Rectangle(Integer.parseInt(dims[0]), Integer.parseInt(dims[1]), Integer.parseInt(dims[2]), Integer.parseInt(dims[3])); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private List encoders; + + /** + * @return array of available encoders. + */ + public List getEncoders() { + return encoders; + } + + Properties store() { + Properties properties = new Properties(); + if (audioFormat!=null) { + properties.setProperty(AUDIO_FORMAT_CHANNELS_PROPERTY, String.valueOf(audioFormat.getChannels())); + properties.setProperty(AUDIO_FORMAT_SAMPLE_RATE_PROPERTY, String.valueOf(audioFormat.getSampleRate())); + properties.setProperty(AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY, String.valueOf(audioFormat.getSampleSizeInBits())); + } + properties.setProperty(BORDER_PROPERTY, String.valueOf(border)); + properties.setProperty(FRAMES_PER_SECOND_PROPERTY, String.valueOf(framesPerSecond)); + properties.setProperty(IMAGE_FORMAT_PROPERTY, String.valueOf(imageFormat)); + properties.setProperty(INACTIVITY_INTERVAL_PROPERTY, String.valueOf(inactivityInterval)); + properties.setProperty(LOOP_PROPERTY, String.valueOf(loop)); + properties.setProperty(MIXER_NAME_PROPERTY, String.valueOf(mixerName)); + properties.setProperty(MOUSE_PROPERTY, String.valueOf(mouse)); + properties.setProperty(PLAY_PROPERTY, String.valueOf(play)); + properties.setProperty(REMOVE_INACTIVITY_PROPERTY, String.valueOf(removeInactivity)); + properties.setProperty(SCREEN_SCALE_PROPERTY, String.valueOf(screenScale)); + properties.setProperty(SOUND_PROPERTY, String.valueOf(sound)); + properties.setProperty(SPEED_SCALE_PROPERTY, String.valueOf(speedScale)); + properties.setProperty(TOOL_BAR_PROPERTY, String.valueOf(toolBar)); + if (recordingRectangle!=null) { + properties.setProperty(RECORDING_RECTANGLE_PROPERTY, recordingRectangle.x+";"+recordingRectangle.y+";"+recordingRectangle.width+";"+recordingRectangle.height); + } + if (mp3command!=null) { + properties.setProperty(MP3_COMMAND_PROPERTY, mp3command); + } + if (encoder!=null) { + properties.setProperty(ENCODER_NAME_PROPERTY, encoder.toString()); + } + + return properties; + } + + +} \ No newline at end of file diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/CaptureFrame.java b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureFrame.java new file mode 100644 index 000000000..23e0d10bf --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureFrame.java @@ -0,0 +1,405 @@ +package com.hammurapi.jcapture; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.border.LineBorder; + +import netscape.javascript.JSObject; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; + +/** + * License: LGPL. + * @author Pavel Vlasov. + * + */ +public class CaptureFrame extends javax.swing.JFrame { + private JPanel capturePanel; + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss-SSS"); + private int counter; + private CaptureConfig captureConfig; + private AbstractCaptureApplet applet; + private JButton recordButton; + + public CaptureConfig getCaptureConfig() { + return captureConfig; + } + + public CaptureFrame(final AbstractCaptureApplet applet) throws Exception { + super("Screen capture"); + setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("camera.png"))); + + setUndecorated(true); + + Translucener.makeFrameTranslucent(this); + + setAlwaysOnTop(true); + this.applet = applet; + captureConfig = new CaptureConfig(); + captureConfig.load(applet.loadConfig()); + captureConfig.setBackgroundProcessor(applet.getBackgroundProcessor()); + + //--- GUI construction --- + + capturePanel = new JPanel(); + + final JLabel dimensionsLabel = new JLabel(""); + capturePanel.add(dimensionsLabel, BorderLayout.CENTER); + + capturePanel.addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + super.componentResized(e); + dimensionsLabel.setText(e.getComponent().getWidth()+" x "+e.getComponent().getHeight()); + } + }); + + JButton captureButton = new JButton(new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + Rectangle bounds = capturePanel.getBounds(); + Point loc = bounds.getLocation(); + SwingUtilities.convertPointToScreen(loc, capturePanel); + bounds.setLocation(loc); + Properties props = captureConfig.setRecordingRectangle(bounds); + if (props!=null) { + getApplet().storeConfig(props); + } + capturing.set(true); + setVisible(false); + } + + }); + captureButton.setText("Capture"); + captureButton.setToolTipText("Create a snapshot of the screen"); + capturePanel.add(captureButton, BorderLayout.CENTER); + + recordButton = new JButton(new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + Rectangle bounds = capturePanel.getBounds(); + Point loc = bounds.getLocation(); + SwingUtilities.convertPointToScreen(loc, capturePanel); + bounds.setLocation(loc); + Properties props = captureConfig.setRecordingRectangle(bounds); + if (props!=null) { + getApplet().storeConfig(props); + } + recording.set(true); + setVisible(false); + } + + }); + recordButton.setText("Record"); + setRecordButtonState(); + capturePanel.add(recordButton, BorderLayout.CENTER); + + JButton optionsButton = new JButton(new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + new CaptureOptionsDialog(CaptureFrame.this).setVisible(true); + } + + }); + optionsButton.setText("Options"); + capturePanel.add(optionsButton, BorderLayout.CENTER); + + JButton cancelButton = new JButton(new AbstractAction() { + + @Override + public void actionPerformed(ActionEvent e) { + CaptureFrame.this.setVisible(false); + } + + }); + cancelButton.setText("Cancel"); + capturePanel.add(cancelButton, BorderLayout.CENTER); + + getContentPane().add(capturePanel, BorderLayout.CENTER); + + capturePanel.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false)); + + if (captureConfig.getRecordingRectangle()==null) { + setSize(400, 300); + setLocationRelativeTo(null); + } else { + setBounds(captureConfig.getRecordingRectangle()); + } + + Insets dragInsets = new Insets(5, 5, 5, 5); + new ComponentResizer(dragInsets, this); + + ComponentMover cm = new ComponentMover(); + cm.registerComponent(this); + cm.setDragInsets(dragInsets); + + addComponentListener(new ComponentListener() { + + @Override + public void componentShown(ComponentEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void componentResized(ComponentEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void componentMoved(ComponentEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void componentHidden(ComponentEvent e) { + if (capturing.get()) { + capturing.set(false); + try { + capture(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } else if (recording.get()) { + recording.set(false); + record(); + } + } + }); + + } + + void setRecordButtonState() { + if (captureConfig.getEncoder()==null) { + recordButton.setEnabled(false); + recordButton.setToolTipText("Video format not selected. Use Options dialog to select video format."); + } else { + recordButton.setEnabled(true); + recordButton.setToolTipText("Record screen activity and audio"); + } + } + + public AbstractCaptureApplet getApplet() { + return applet; + } + + protected void capture() throws Exception { + try { + Thread.sleep(200); // For Ubuntu. + } catch (InterruptedException ie) { + // Ignore + } + + BufferedImage screenShot = captureConfig.createScreenShot(null, null).call().getRegions().get(0).getImage().getImage(); + + String prefix = getDatePrefix(); + + String defaultImageFormat = applet.getParameter("imageFormat"); + if (defaultImageFormat==null || defaultImageFormat.trim().length()==0) { + defaultImageFormat = "PNG"; + } + final String defaultFileExtension=defaultImageFormat.toLowerCase(); + + final String fileName = JOptionPane.showInputDialog(CaptureFrame.this, "Upload as", applet.getParameter("pageName")+"-capture-"+prefix+"-" + nextCounter() +"."+defaultFileExtension); + if (fileName!=null) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int idx = fileName.lastIndexOf('.'); + String imageFormat = idx==-1 ? defaultImageFormat : fileName.substring(idx+1).toUpperCase(); + ImageIO.write(screenShot, imageFormat, baos); + final byte[] imageBytes = baos.toByteArray(); + System.out.println("Image size: "+imageBytes.length); + // Uploading + SwingWorker task = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + + System.out.println("Uploading in background"); + try { + HttpResponse iResponse = applet.post( + CaptureFrame.this, + new ByteArrayInputStream(imageBytes), + imageBytes.length, + fileName, + "application/octet-stream"); + + System.out.println("Response status line: "+iResponse.getStatusLine()); + if (iResponse.getStatusLine().getStatusCode()!=HttpStatus.SC_OK) { + errorMessage = iResponse.getStatusLine(); + errorTitle = "Error saving image"; + return false; + } + return true; + } catch (Error e) { + errorMessage=e.toString(); + errorTitle = "Upload error"; + e.printStackTrace(); + return false; + } + } + + private Object errorMessage; + private String errorTitle; + + protected void done() { + try { + if (get()) { + JSObject window = JSObject.getWindow(applet); + String toEval = "insertAtCarret('"+applet.getParameter("edid")+"','{{:"+fileName+"|}}')"; + System.out.println("Evaluating: "+toEval); + window.eval(toEval); + CaptureFrame.this.setVisible(false); + } else { + JOptionPane.showMessageDialog( + CaptureFrame.this, + errorMessage, + errorTitle, + JOptionPane.ERROR_MESSAGE); + } + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog( + CaptureFrame.this, + e.toString(), + "Exception", + JOptionPane.ERROR_MESSAGE); + } + }; + + }; + + task.execute(); + } catch (IOException ex) { + JOptionPane.showMessageDialog( + applet, + ex.toString(), + "Error saving image", + JOptionPane.ERROR_MESSAGE); + } + } + } + + public int nextCounter() { + return counter++; + } + + public String getDatePrefix() { + return dateFormat.format(new Date()); + } + + protected void record() { + try { + Thread.sleep(200); // For Ubuntu. + } catch (InterruptedException ie) { + // Ignore + } + + int borderWidth = 1; + JFrame[] borderFrames = new JFrame[4]; + + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + + Rectangle rr = captureConfig.getRecordingRectangle(); + Color borderColor = Color.RED; + if (rr.x>=borderWidth) { + // West border + borderFrames[0] = new JFrame(); + borderFrames[0].setDefaultCloseOperation(DISPOSE_ON_CLOSE); + borderFrames[0].setSize(borderWidth, rr.height+borderWidth*2); + borderFrames[0].setLocation(rr.x-borderWidth, rr.y-borderWidth); + borderFrames[0].setUndecorated(true); + borderFrames[0].setAlwaysOnTop(true); + borderFrames[0].setFocusableWindowState(false); + borderFrames[0].getContentPane().setBackground(borderColor); + } + if (rr.x+rr.width=borderWidth) { + // North border + borderFrames[2] = new JFrame(); + borderFrames[2].setDefaultCloseOperation(DISPOSE_ON_CLOSE); + borderFrames[2].setSize(rr.width, borderWidth); + borderFrames[2].setLocation(rr.x, rr.y-borderWidth); + borderFrames[2].setUndecorated(true); + borderFrames[2].setAlwaysOnTop(true); + borderFrames[2].setFocusableWindowState(false); + borderFrames[2].getContentPane().setBackground(borderColor); + } + if (rr.y+rr.heightdim.getWidth()) { + x = dim.width-inst.getWidth(); + } else if (x<0) { + x = 0; + } + + int y = rr.getLocation().y+getHeight()+1; + if (y+inst.getHeight()>dim.height) { + y = rr.getLocation().y-inst.getHeight(); + if (y<0) { + y=dim.height-inst.getHeight(); + } + } + inst.setLocation(x, y); + inst.setVisible(true); + } + + private AtomicBoolean capturing = new AtomicBoolean(false); + private AtomicBoolean recording = new AtomicBoolean(false); + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/CaptureOptionsDialog.java b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureOptionsDialog.java new file mode 100644 index 000000000..a4298de0e --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/CaptureOptionsDialog.java @@ -0,0 +1,606 @@ +package com.hammurapi.jcapture; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.TargetDataLine; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingConstants; + +public class CaptureOptionsDialog extends javax.swing.JDialog { + private JRadioButton sampleSize16Button; + private JTextField timeLineScaleTextField; + private JCheckBox toobarCheckBox; + private JCheckBox videoBorderCheckBox; + private JTextField screenScaleTextField; + private JLabel timelineScalingLabel; + private JLabel screenScalingLabel; + private JTextField fpsTextField; + private JLabel fpsLabel; + private ButtonGroup sampleSizeButtonGroup; + private JComboBox sampleRateComboBox; + private JRadioButton sampleSize8Button; + private JTextField inactivityIntervalTextField; + private JLabel inactivityIntervalLabel; + private JCheckBox inactivityCheckBox; + private JPanel inactivityPanel; + private JPanel scalingPanel; + private JCheckBox stereoCheckBox; + private JLabel sampleSizeLabel; + private JTabbedPane recordingSettingsPane; + private JCheckBox recordSoundCheckBox; + private JLabel sampleRateLabel; + private JComboBox soundLineComboBox; + private JLabel soundSourceLabel; + private JPanel audioSettingsPanel; + private JPanel videoSettingsPanel; + private JButton cancelButton; + private JButton okButton; + private JPanel recordPanel; + private JComboBox encodersComboBox; + private JTextField mp3Text; + + public CaptureOptionsDialog(final CaptureFrame owner) { + super(owner); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + BorderLayout thisLayout = new BorderLayout(); + this.setLayout(thisLayout); + this.setPreferredSize(new java.awt.Dimension(333, 186)); + + recordPanel = new JPanel(); + this.add(recordPanel); + GridBagLayout recordPanelLayout = new GridBagLayout(); + recordPanelLayout.rowWeights = new double[] { 0.1, 0.0, 0.0, 0.0 }; + recordPanelLayout.rowHeights = new int[] { 7, 7, 20, 7 }; + recordPanelLayout.columnWeights = new double[] { 0.1, 0.0, 0.0, 0.0, 0.0 }; + recordPanelLayout.columnWidths = new int[] { 20, 7, 7, 7, 7 }; + recordPanel.setLayout(recordPanelLayout); + recordPanel.setPreferredSize(new java.awt.Dimension(335, 297)); + + okButton = new JButton(); + recordPanel.add(okButton, new GridBagConstraints(1, 2, 1, 1, 0.0, + 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + okButton.setText("OK"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + try { + if (recordSoundCheckBox.isSelected()) { + owner.getCaptureConfig().setAudioFormat(audioFormat); + owner.getCaptureConfig().setMixerName((String) soundLineComboBox.getSelectedItem()); + } else { + owner.getCaptureConfig().setRemoveInactivity(inactivityCheckBox.isSelected()); + if (owner.getCaptureConfig().isRemoveInactivity()) { + owner.getCaptureConfig().setInactivityInterval(Double.parseDouble(inactivityIntervalTextField.getText())); + } + } + + owner.getCaptureConfig().setLoop(getLoopCheckBox().isSelected()); + owner.getCaptureConfig().setPlay(getPlayCheckBox().isSelected()); + owner.getCaptureConfig().setMouse(getMouseCheckBox().isSelected()); + owner.getCaptureConfig().setSound(recordSoundCheckBox.isSelected()); + owner.getCaptureConfig().setImageFormat(getImageFormatTextField().getText().trim()); + owner.getCaptureConfig().setBorder(videoBorderCheckBox.isSelected()); + owner.getCaptureConfig().setFramesPerSecond(Float.parseFloat(fpsTextField.getText())); + owner.getCaptureConfig().setScreenScale(Double.parseDouble(screenScaleTextField.getText()) / 100.0); + owner.getCaptureConfig().setSpeedScale((float) (Float.parseFloat(timeLineScaleTextField.getText()) / 100.0)); + owner.getCaptureConfig().setToolBar(toobarCheckBox.isSelected()); + owner.getApplet().storeConfig(owner.getCaptureConfig().store()); + owner.getCaptureConfig().setMp3command(mp3Text.getText()); + owner.getCaptureConfig().setEncoder((VideoEncoder) encodersComboBox.getSelectedItem()); + owner.setRecordButtonState(); + CaptureOptionsDialog.this.setVisible(false); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(CaptureOptionsDialog.this, + e.toString(), "Error in configuration parameters", + JOptionPane.ERROR_MESSAGE); + } + } + }); + + cancelButton = new JButton(); + recordPanel.add(cancelButton, new GridBagConstraints(3, 2, 1, 1, 0.0, + 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + cancelButton.setText("Cancel"); + cancelButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + CaptureOptionsDialog.this.setVisible(false); + } + }); + + recordingSettingsPane = new JTabbedPane(); + recordPanel.add(recordingSettingsPane, new GridBagConstraints(0, 0, 5, + 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + + videoSettingsPanel = new JPanel(); + GridBagLayout videoSettingsPanelLayout = new GridBagLayout(); + recordingSettingsPane.addTab("Video", null, videoSettingsPanel, null); + videoSettingsPanel.setPreferredSize(new java.awt.Dimension(112, 207)); + videoSettingsPanelLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1 }; + videoSettingsPanelLayout.rowHeights = new int[] { 7, 7, 7, 7, 7, 7, 7, 20 }; + videoSettingsPanelLayout.columnWeights = new double[] { 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.1 }; + videoSettingsPanelLayout.columnWidths = new int[] { 115, 7, 40, 7, 20, + 7, 20, 7, 20 }; + videoSettingsPanel.setLayout(videoSettingsPanelLayout); + + scalingPanel = new JPanel(); + GridBagLayout scalingPanelLayout = new GridBagLayout(); + videoSettingsPanel.add(scalingPanel, new GridBagConstraints(0, 6, 1, 1, + 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + scalingPanel.setBorder(BorderFactory.createTitledBorder("Scaling (%)")); + scalingPanelLayout.rowWeights = new double[] { 0.1, 0.0, 0.1 }; + scalingPanelLayout.rowHeights = new int[] { 7, 7, 7 }; + scalingPanelLayout.columnWeights = new double[] { 0.0, 0.0, 0.1 }; + scalingPanelLayout.columnWidths = new int[] { 7, 7, 7 }; + scalingPanel.setLayout(scalingPanelLayout); + scalingPanel.add(getScreenScalingLabel(), new GridBagConstraints(0, 0, + 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + scalingPanel.add(getTimelineScalingLabel(), new GridBagConstraints(0, + 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + scalingPanel.add(getScreenScaleTextField(), new GridBagConstraints(2, + 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + scalingPanel.add(getTimeLineScaleTextField(), new GridBagConstraints(2, + 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + inactivityPanel = new JPanel(); + GridBagLayout inactivityPanelLayout = new GridBagLayout(); + videoSettingsPanel.add(inactivityPanel, new GridBagConstraints(2, 6, 8, + 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getFpsLabel(), new GridBagConstraints(0, 0, 1, + 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getFpsTextField(), new GridBagConstraints(2, 0, + 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + videoSettingsPanel.add(getImageFormatLabel(), new GridBagConstraints(0, 2, 1, + 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getImageFormatTextField(), new GridBagConstraints(2, 2, + 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + videoSettingsPanel.add(getEncoderLabel(), new GridBagConstraints(0, 4, 1, + 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getEncoderComboBox(), new GridBagConstraints(2, 4, + 6, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + videoSettingsPanel.add(getVideoBorderCheckBox(), + new GridBagConstraints(4, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.WEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getLoopCheckBox(), + new GridBagConstraints(6, 2, 1, 1, 0.0, 0.0, + GridBagConstraints.WEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getPlayCheckBox(), + new GridBagConstraints(8, 2, 1, 1, 0.0, 0.0, + GridBagConstraints.WEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getMouseCheckBox(), + new GridBagConstraints(4, 2, 1, 1, 0.0, 0.0, + GridBagConstraints.WEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + videoSettingsPanel.add(getJToobarCheckBox(), new GridBagConstraints(6, + 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + inactivityPanel.setBorder(BorderFactory + .createTitledBorder("Inactivity processing")); + inactivityPanel + .setToolTipText("Inactivity handling, enabled if audio is not being recorded."); + inactivityPanelLayout.rowWeights = new double[] { 0.0, 0.0, 0.0 }; + inactivityPanelLayout.rowHeights = new int[] { 7, 7, 7 }; + inactivityPanelLayout.columnWeights = new double[] { 0.0, 0.0, 0.0, + 0.0, 0.1 }; + inactivityPanelLayout.columnWidths = new int[] { 7, 7, 7, 47, 7 }; + inactivityPanel.setLayout(inactivityPanelLayout); + inactivityPanel.setEnabled(false); + + inactivityCheckBox = new JCheckBox(); + inactivityPanel.add(inactivityCheckBox, new GridBagConstraints(1, 0, 4, + 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + inactivityCheckBox.setText("Remove inactivity"); + inactivityCheckBox.setEnabled(false); + + inactivityIntervalLabel = new JLabel(); + inactivityPanel.add(inactivityIntervalLabel, new GridBagConstraints(1, + 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + inactivityIntervalLabel.setText("Inactivity interval (sec)"); + inactivityIntervalLabel.setEnabled(false); + + inactivityIntervalTextField = new JTextField(); + inactivityPanel.add(inactivityIntervalTextField, + new GridBagConstraints(3, 2, 1, 1, 0.0, 0.0, + GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), + 0, 0)); + inactivityIntervalTextField.setText("0.7"); + inactivityIntervalTextField.setEnabled(false); + + audioSettingsPanel = new JPanel(); + GridBagLayout audioSettingsPanelLayout = new GridBagLayout(); + recordingSettingsPane.addTab("Audio", null, audioSettingsPanel, null); + audioSettingsPanelLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1 }; + audioSettingsPanelLayout.rowHeights = new int[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, + 20 }; + audioSettingsPanelLayout.columnWeights = new double[] { 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.1 }; + audioSettingsPanelLayout.columnWidths = new int[] { 7, 7, 49, 7, 135, + 7, 20 }; + audioSettingsPanel.setLayout(audioSettingsPanelLayout); + + sampleSize16Button = new JRadioButton(); + audioSettingsPanel.add(sampleSize16Button, new GridBagConstraints(4, 4, + 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + sampleSize16Button.setText("16"); + sampleSize16Button.setSelected(true); + getSampleSizeButtonGroup().add(sampleSize16Button); + sampleSize16Button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + selectSoundSource(); + } + }); + + recordSoundCheckBox = new JCheckBox(); + audioSettingsPanel.add(recordSoundCheckBox, new GridBagConstraints(0, + 0, 4, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + recordSoundCheckBox.setText("Record sound"); + recordSoundCheckBox.setSelected(true); + recordSoundCheckBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + onSetSound(); + } + }); + + soundSourceLabel = new JLabel(); + audioSettingsPanel.add(soundSourceLabel, new GridBagConstraints(0, 6, + 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + soundSourceLabel.setText("Source"); + + soundLineComboBox = new JComboBox(); + audioSettingsPanel.add(soundLineComboBox, new GridBagConstraints(2, 6, + 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + audioSettingsPanel.add(new JLabel("WAV2MP3 command"), new GridBagConstraints(0, 8, + 1, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + + mp3Text = new JTextField(); + audioSettingsPanel.add(mp3Text, new GridBagConstraints(2, 8, + 5, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + + sampleRateLabel = new JLabel(); + audioSettingsPanel.add(sampleRateLabel, new GridBagConstraints(0, 2, 1, + 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + sampleRateLabel.setText("Sample rate (khz)"); + + sampleSizeLabel = new JLabel(); + audioSettingsPanel.add(sampleSizeLabel, new GridBagConstraints(0, 4, 1, + 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + sampleSizeLabel.setText("Sample size (bits)"); + + stereoCheckBox = new JCheckBox(); + audioSettingsPanel.add(stereoCheckBox, new GridBagConstraints(4, 2, 1, + 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0)); + stereoCheckBox.setText("Stereo"); + stereoCheckBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + selectSoundSource(); + } + }); + + sampleSize8Button = new JRadioButton(); + audioSettingsPanel.add(sampleSize8Button, new GridBagConstraints(2, 4, + 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + sampleSize8Button.setText("8"); + getSampleSizeButtonGroup().add(sampleSize8Button); + sampleSize8Button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + selectSoundSource(); + } + }); + + ComboBoxModel sampleRateComboBoxModel = new DefaultComboBoxModel( + new String[] { "5.5", "11", "22", "44" }); + sampleRateComboBox = new JComboBox(); + audioSettingsPanel.add(sampleRateComboBox, new GridBagConstraints(2, 2, + 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + sampleRateComboBox.setModel(sampleRateComboBoxModel); + sampleRateComboBox.setSelectedIndex(2); + sampleRateComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + selectSoundSource(); + } + }); + + getImageFormatTextField().setText(owner.getCaptureConfig().getImageFormat()); + + recordSoundCheckBox.setSelected(owner.getCaptureConfig().isSound()); + stereoCheckBox.setSelected(owner.getCaptureConfig().getAudioFormat().getChannels()>1); + if (owner.getCaptureConfig().getAudioFormat().getSampleSizeInBits()==8) { + sampleSize8Button.setSelected(true); + } else { + sampleSize16Button.setSelected(true); + } + + float sampleRate = owner.getCaptureConfig().getAudioFormat().getSampleRate(); + float proximity = Math.abs(sampleRate-sampleRates[0]); + sampleRateComboBox.setSelectedIndex(0); + for (int i=1; i soundLineComboBoxModel; + private JLabel imageFormatLabel; + private JTextField imageFormatTextField; + private JCheckBox mouseCheckBox; + private JCheckBox playCheckBox; + private JCheckBox loopCheckBox; + private JLabel encoderLabel; + + private void selectSoundSource() { + audioFormat = new AudioFormat( + sampleRates[sampleRateComboBox.getSelectedIndex()], + sampleSize8Button.isSelected() ? 8 : 16, + stereoCheckBox.isSelected() ? 2 : 1, true, false); + + String sourceName = (String) soundLineComboBox.getSelectedItem(); + + if (soundLineComboBoxModel == null) { + soundLineComboBoxModel = new DefaultComboBoxModel(); + soundLineComboBox.setModel(soundLineComboBoxModel); + } else { + soundLineComboBoxModel.removeAllElements(); + } + + DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat); + + boolean hasSourceName = false; + for (Mixer.Info mi : AudioSystem.getMixerInfo()) { + Mixer mx = AudioSystem.getMixer(mi); + if (mx.isLineSupported(info)) { + soundLineComboBoxModel.addElement(mi.getName()); + if (sourceName!=null && mi.getName().equals(sourceName)) { + hasSourceName = true; + } + } + } + + if (hasSourceName) { + soundLineComboBoxModel.setSelectedItem(sourceName); + } + } + + private ButtonGroup getSampleSizeButtonGroup() { + if (sampleSizeButtonGroup == null) { + sampleSizeButtonGroup = new ButtonGroup(); + } + return sampleSizeButtonGroup; + } + + private JLabel getFpsLabel() { + if (fpsLabel == null) { + fpsLabel = new JLabel(); + fpsLabel.setText("Frames Per Second"); + } + return fpsLabel; + } + + private JTextField getFpsTextField() { + if (fpsTextField == null) { + fpsTextField = new JTextField(); + fpsTextField.setText("10"); + fpsTextField.setSize(30, 23); + } + return fpsTextField; + } + + private JLabel getImageFormatLabel() { + if (imageFormatLabel == null) { + imageFormatLabel = new JLabel(); + imageFormatLabel.setText("Image format"); + } + return imageFormatLabel; + } + + private JTextField getImageFormatTextField() { + if (imageFormatTextField == null) { + imageFormatTextField = new JTextField(); + imageFormatTextField.setText("png"); + imageFormatTextField.setSize(30, 23); + } + return imageFormatTextField; + } + + private JLabel getEncoderLabel() { + if (encoderLabel == null) { + encoderLabel = new JLabel(); + encoderLabel.setText("Video format"); + } + return encoderLabel; + } + + private JComboBox getEncoderComboBox() { + if (encodersComboBox == null) { + List el = ((CaptureFrame) getOwner()).getCaptureConfig().getEncoders(); + encodersComboBox = new JComboBox(el.toArray(new VideoEncoder[el.size()])); +// encodersComboBox.setSize(30, 23); + } + return encodersComboBox; + } + + private JLabel getScreenScalingLabel() { + if (screenScalingLabel == null) { + screenScalingLabel = new JLabel(); + screenScalingLabel.setText("Graphics"); + } + return screenScalingLabel; + } + + private JLabel getTimelineScalingLabel() { + if (timelineScalingLabel == null) { + timelineScalingLabel = new JLabel(); + timelineScalingLabel.setText("Speed"); + } + return timelineScalingLabel; + } + + private JTextField getScreenScaleTextField() { + if (screenScaleTextField == null) { + screenScaleTextField = new JTextField(); + screenScaleTextField.setText("100"); + } + return screenScaleTextField; + } + + private JTextField getTimeLineScaleTextField() { + if (timeLineScaleTextField == null) { + timeLineScaleTextField = new JTextField(); + timeLineScaleTextField.setText("100"); + } + return timeLineScaleTextField; + } + + private JCheckBox getVideoBorderCheckBox() { + if (videoBorderCheckBox == null) { + videoBorderCheckBox = new JCheckBox(); + videoBorderCheckBox.setText("Border"); + } + return videoBorderCheckBox; + } + + private JCheckBox getMouseCheckBox() { + if (mouseCheckBox == null) { + mouseCheckBox = new JCheckBox(); + mouseCheckBox.setText("Mouse"); + } + return mouseCheckBox; + } + + private JCheckBox getLoopCheckBox() { + if (loopCheckBox == null) { + loopCheckBox = new JCheckBox(); + loopCheckBox.setText("Loop"); + } + return loopCheckBox; + } + + private JCheckBox getPlayCheckBox() { + if (playCheckBox == null) { + playCheckBox = new JCheckBox(); + playCheckBox.setText("Play"); + } + return playCheckBox; + } + + private JCheckBox getJToobarCheckBox() { + if (toobarCheckBox == null) { + toobarCheckBox = new JCheckBox(); + toobarCheckBox.setText("Toolbar"); + toobarCheckBox.setSelected(true); + } + return toobarCheckBox; + } + + void onSetSound() { + for (Component child : recordSoundCheckBox.getParent().getComponents()) { + if (child != recordSoundCheckBox) { + child.setEnabled(recordSoundCheckBox.isSelected()); + } + } + inactivityPanel.setEnabled(!recordSoundCheckBox.isSelected()); + timeLineScaleTextField.setEnabled(!recordSoundCheckBox.isSelected()); + for (Component child : inactivityPanel.getComponents()) { + child.setEnabled(!recordSoundCheckBox.isSelected()); + } + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ComponentMover.java b/plugin/jcapture/src/com/hammurapi/jcapture/ComponentMover.java new file mode 100644 index 000000000..29526cc1d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ComponentMover.java @@ -0,0 +1,361 @@ +package com.hammurapi.jcapture; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/** + * This class allows you to move a Component by using a mouse. The Component + * moved can be a high level Window (ie. Window, Frame, Dialog) in which case + * the Window is moved within the desktop. Or the Component can belong to a + * Container in which case the Component is moved within the Container. + * + * When moving a Window, the listener can be added to a child Component of the + * Window. In this case attempting to move the child will result in the Window + * moving. For example, you might create a custom "Title Bar" for an undecorated + * Window and moving of the Window is accomplished by moving the title bar only. + * Multiple components can be registered as "window movers". + * + * Components can be registered when the class is created. Additional components + * can be added at any time using the registerComponent() method. + * + * Taken from http://tips4java.wordpress.com/2009/06/14/moving-windows/ + */ +public class ComponentMover extends MouseAdapter { + private Insets dragInsets = new Insets(0, 0, 0, 0); + private Dimension snapSize = new Dimension(1, 1); + private Insets edgeInsets = new Insets(0, 0, 0, 0); + private boolean changeCursor = true; + private boolean autoLayout = false; + + private Class destinationClass; + private Component destinationComponent; + private Component destination; + private Component source; + + private Point pressed; + private Point location; + + private Cursor originalCursor; + private boolean autoscrolls; + private boolean potentialDrag; + + /** + * Constructor for moving individual components. The components must be + * regisetered using the registerComponent() method. + */ + public ComponentMover() { + } + + /** + * Constructor to specify a Class of Component that will be moved when drag + * events are generated on a registered child component. The events will be + * passed to the first ancestor of this specified class. + * + * @param destinationClass + * the Class of the ancestor component + * @param component + * the Components to be registered for forwarding drag events to + * the ancestor Component. + */ + public ComponentMover(Class destinationClass, Component... components) { + this.destinationClass = destinationClass; + registerComponent(components); + } + + /** + * Constructor to specify a parent component that will be moved when drag + * events are generated on a registered child component. + * + * @param destinationComponent + * the component drage events should be forwareded to + * @param components + * the Components to be registered for forwarding drag events to + * the parent component to be moved + */ + public ComponentMover(Component destinationComponent, + Component... components) { + this.destinationComponent = destinationComponent; + registerComponent(components); + } + + /** + * Get the auto layout property + * + * @return the auto layout property + */ + public boolean isAutoLayout() { + return autoLayout; + } + + /** + * Set the auto layout property + * + * @param autoLayout + * when true layout will be invoked on the parent container + */ + public void setAutoLayout(boolean autoLayout) { + this.autoLayout = autoLayout; + } + + /** + * Get the change cursor property + * + * @return the change cursor property + */ + public boolean isChangeCursor() { + return changeCursor; + } + + /** + * Set the change cursor property + * + * @param changeCursor + * when true the cursor will be changed to the Cursor.MOVE_CURSOR + * while the mouse is pressed + */ + public void setChangeCursor(boolean changeCursor) { + this.changeCursor = changeCursor; + } + + /** + * Get the drag insets + * + * @return the drag insets + */ + public Insets getDragInsets() { + return dragInsets; + } + + /** + * Set the drag insets. The insets specify an area where mouseDragged events + * should be ignored and therefore the component will not be moved. This + * will prevent these events from being confused with a MouseMotionListener + * that supports component resizing. + * + * @param dragInsets + */ + public void setDragInsets(Insets dragInsets) { + this.dragInsets = dragInsets; + } + + /** + * Get the bounds insets + * + * @return the bounds insets + */ + public Insets getEdgeInsets() { + return edgeInsets; + } + + /** + * Set the edge insets. The insets specify how close to each edge of the + * parent component that the child component can be moved. Positive values + * means the component must be contained within the parent. Negative values + * means the component can be moved outside the parent. + * + * @param edgeInsets + */ + public void setEdgeInsets(Insets edgeInsets) { + this.edgeInsets = edgeInsets; + } + + /** + * Remove listeners from the specified component + * + * @param component + * the component the listeners are removed from + */ + public void deregisterComponent(Component... components) { + for (Component component : components) + component.removeMouseListener(this); + } + + /** + * Add the required listeners to the specified component + * + * @param component + * the component the listeners are added to + */ + public void registerComponent(Component... components) { + for (Component component : components) + component.addMouseListener(this); + } + + /** + * Get the snap size + * + * @return the snap size + */ + public Dimension getSnapSize() { + return snapSize; + } + + /** + * Set the snap size. Forces the component to be snapped to the closest grid + * position. Snapping will occur when the mouse is dragged half way. + */ + public void setSnapSize(Dimension snapSize) { + if (snapSize.width < 1 || snapSize.height < 1) + throw new IllegalArgumentException( + "Snap sizes must be greater than 0"); + + this.snapSize = snapSize; + } + + /** + * Setup the variables used to control the moving of the component: + * + * source - the source component of the mouse event destination - the + * component that will ultimately be moved pressed - the Point where the + * mouse was pressed in the destination component coordinates. + */ + @Override + public void mousePressed(MouseEvent e) { + source = e.getComponent(); + int width = source.getSize().width - dragInsets.left - dragInsets.right; + int height = source.getSize().height - dragInsets.top + - dragInsets.bottom; + Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, + height); + + if (r.contains(e.getPoint())) + setupForDragging(e); + } + + private void setupForDragging(MouseEvent e) { + source.addMouseMotionListener(this); + potentialDrag = true; + + // Determine the component that will ultimately be moved + + if (destinationComponent != null) { + destination = destinationComponent; + } else if (destinationClass == null) { + destination = source; + } else // forward events to destination component + { + destination = SwingUtilities.getAncestorOfClass(destinationClass, + source); + } + + pressed = e.getLocationOnScreen(); + location = destination.getLocation(); + + if (changeCursor) { + originalCursor = source.getCursor(); + source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } + + // Making sure autoscrolls is false will allow for smoother dragging of + // individual components + + if (destination instanceof JComponent) { + JComponent jc = (JComponent) destination; + autoscrolls = jc.getAutoscrolls(); + jc.setAutoscrolls(false); + } + } + + /** + * Move the component to its new location. The dragged Point must be in the + * destination coordinates. + */ + @Override + public void mouseDragged(MouseEvent e) { + Point dragged = e.getLocationOnScreen(); + int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width); + int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height); + + int locationX = location.x + dragX; + int locationY = location.y + dragY; + + // Mouse dragged events are not generated for every pixel the mouse + // is moved. Adjust the location to make sure we are still on a + // snap value. + +// while (locationX < edgeInsets.left) +// locationX += snapSize.width; +// +// while (locationY < edgeInsets.top) +// locationY += snapSize.height; +// +// Dimension d = getBoundingSize(destination); + +// while (locationX + destination.getSize().width + edgeInsets.right > d.width) +// locationX -= snapSize.width; +// +// while (locationY + destination.getSize().height + edgeInsets.bottom > d.height) +// locationY -= snapSize.height; + + // Adjustments are finished, move the component + + destination.setLocation(locationX, locationY); + } + + /* + * Determine how far the mouse has moved from where dragging started (Assume + * drag direction is down and right for positive drag distance) + */ + private int getDragDistance(int larger, int smaller, int snapSize) { + int halfway = snapSize / 2; + int drag = larger - smaller; + drag += (drag < 0) ? -halfway : halfway; + drag = (drag / snapSize) * snapSize; + + return drag; + } + + /* + * Get the bounds of the parent of the dragged component. + */ + private Dimension getBoundingSize(Component source) { + if (source instanceof Window) { + GraphicsEnvironment env = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + Rectangle bounds = env.getMaximumWindowBounds(); + return new Dimension(bounds.width, bounds.height); + } else { + return source.getParent().getSize(); + } + } + + /** + * Restore the original state of the Component + */ + @Override + public void mouseReleased(MouseEvent e) { + if (!potentialDrag) + return; + + source.removeMouseMotionListener(this); + potentialDrag = false; + + if (changeCursor) + source.setCursor(originalCursor); + + if (destination instanceof JComponent) { + ((JComponent) destination).setAutoscrolls(autoscrolls); + } + + // Layout the components on the parent container + + if (autoLayout) { + if (destination instanceof JComponent) { + ((JComponent) destination).revalidate(); + } else { + destination.validate(); + } + } + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ComponentResizer.java b/plugin/jcapture/src/com/hammurapi/jcapture/ComponentResizer.java new file mode 100644 index 000000000..513c2bdb5 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ComponentResizer.java @@ -0,0 +1,446 @@ +package com.hammurapi.jcapture; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/** + * The ComponentResizer allows you to resize a component by dragging a border of + * the component. + * + * Taken from http://tips4java.wordpress.com/2009/09/13/resizing-components/ + */ +public class ComponentResizer extends MouseAdapter { + private final static Dimension MINIMUM_SIZE = new Dimension(10, 10); + private final static Dimension MAXIMUM_SIZE = new Dimension( + Integer.MAX_VALUE, Integer.MAX_VALUE); + + private static Map cursors = new HashMap(); + { + cursors.put(1, Cursor.N_RESIZE_CURSOR); + cursors.put(2, Cursor.W_RESIZE_CURSOR); + cursors.put(4, Cursor.S_RESIZE_CURSOR); + cursors.put(8, Cursor.E_RESIZE_CURSOR); + cursors.put(3, Cursor.NW_RESIZE_CURSOR); + cursors.put(9, Cursor.NE_RESIZE_CURSOR); + cursors.put(6, Cursor.SW_RESIZE_CURSOR); + cursors.put(12, Cursor.SE_RESIZE_CURSOR); + } + + private Insets dragInsets; + private Dimension snapSize; + + private int direction; + protected static final int NORTH = 1; + protected static final int WEST = 2; + protected static final int SOUTH = 4; + protected static final int EAST = 8; + + private Cursor sourceCursor; + private boolean resizing; + private Rectangle bounds; + private Point pressed; + private boolean autoscrolls; + + private Dimension minimumSize = MINIMUM_SIZE; + private Dimension maximumSize = MAXIMUM_SIZE; + + /** + * Convenience contructor. All borders are resizable in increments of a + * single pixel. Components must be registered separately. + */ + public ComponentResizer() { + this(new Insets(5, 5, 5, 5), new Dimension(1, 1)); + } + + /** + * Convenience contructor. All borders are resizable in increments of a + * single pixel. Components can be registered when the class is created or + * they can be registered separately afterwards. + * + * @param components + * components to be automatically registered + */ + public ComponentResizer(Component... components) { + this(new Insets(5, 5, 5, 5), new Dimension(1, 1), components); + } + + /** + * Convenience contructor. Eligible borders are resisable in increments of a + * single pixel. Components can be registered when the class is created or + * they can be registered separately afterwards. + * + * @param dragInsets + * Insets specifying which borders are eligible to be resized. + * @param components + * components to be automatically registered + */ + public ComponentResizer(Insets dragInsets, Component... components) { + this(dragInsets, new Dimension(1, 1), components); + } + + /** + * Create a ComponentResizer. + * + * @param dragInsets + * Insets specifying which borders are eligible to be resized. + * @param snapSize + * Specify the dimension to which the border will snap to when + * being dragged. Snapping occurs at the halfway mark. + * @param components + * components to be automatically registered + */ + public ComponentResizer(Insets dragInsets, Dimension snapSize, + Component... components) { + setDragInsets(dragInsets); + setSnapSize(snapSize); + registerComponent(components); + } + + /** + * Get the drag insets + * + * @return the drag insets + */ + public Insets getDragInsets() { + return dragInsets; + } + + /** + * Set the drag dragInsets. The insets specify an area where mouseDragged + * events are recognized from the edge of the border inwards. A value of 0 + * for any size will imply that the border is not resizable. Otherwise the + * appropriate drag cursor will appear when the mouse is inside the + * resizable border area. + * + * @param dragInsets + * Insets to control which borders are resizeable. + */ + public void setDragInsets(Insets dragInsets) { + validateMinimumAndInsets(minimumSize, dragInsets); + + this.dragInsets = dragInsets; + } + + /** + * Get the components maximum size. + * + * @return the maximum size + */ + public Dimension getMaximumSize() { + return maximumSize; + } + + /** + * Specify the maximum size for the component. The component will still be + * constrained by the size of its parent. + * + * @param maximumSize + * the maximum size for a component. + */ + public void setMaximumSize(Dimension maximumSize) { + this.maximumSize = maximumSize; + } + + /** + * Get the components minimum size. + * + * @return the minimum size + */ + public Dimension getMinimumSize() { + return minimumSize; + } + + /** + * Specify the minimum size for the component. The minimum size is + * constrained by the drag insets. + * + * @param minimumSize + * the minimum size for a component. + */ + public void setMinimumSize(Dimension minimumSize) { + validateMinimumAndInsets(minimumSize, dragInsets); + + this.minimumSize = minimumSize; + } + + /** + * Remove listeners from the specified component + * + * @param component + * the component the listeners are removed from + */ + public void deregisterComponent(Component... components) { + for (Component component : components) { + component.removeMouseListener(this); + component.removeMouseMotionListener(this); + } + } + + /** + * Add the required listeners to the specified component + * + * @param component + * the component the listeners are added to + */ + public void registerComponent(Component... components) { + for (Component component : components) { + component.addMouseListener(this); + component.addMouseMotionListener(this); + } + } + + /** + * Get the snap size. + * + * @return the snap size. + */ + public Dimension getSnapSize() { + return snapSize; + } + + /** + * Control how many pixels a border must be dragged before the size of the + * component is changed. The border will snap to the size once dragging has + * passed the halfway mark. + * + * @param snapSize + * Dimension object allows you to separately spcify a horizontal + * and vertical snap size. + */ + public void setSnapSize(Dimension snapSize) { + this.snapSize = snapSize; + } + + /** + * When the components minimum size is less than the drag insets then we + * can't determine which border should be resized so we need to prevent this + * from happening. + */ + private void validateMinimumAndInsets(Dimension minimum, Insets drag) { + int minimumWidth = drag.left + drag.right; + int minimumHeight = drag.top + drag.bottom; + + if (minimum.width < minimumWidth || minimum.height < minimumHeight) { + String message = "Minimum size cannot be less than drag insets"; + throw new IllegalArgumentException(message); + } + } + + /** + */ + @Override + public void mouseMoved(MouseEvent e) { + Component source = e.getComponent(); + Point location = e.getPoint(); + direction = 0; + + if (location.x < dragInsets.left) + direction += WEST; + + if (location.x > source.getWidth() - dragInsets.right - 1) + direction += EAST; + + if (location.y < dragInsets.top) + direction += NORTH; + + if (location.y > source.getHeight() - dragInsets.bottom - 1) + direction += SOUTH; + + // Mouse is no longer over a resizable border + + if (direction == 0) { + source.setCursor(sourceCursor); + } else // use the appropriate resizable cursor + { + int cursorType = cursors.get(direction); + Cursor cursor = Cursor.getPredefinedCursor(cursorType); + source.setCursor(cursor); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + if (!resizing) { + Component source = e.getComponent(); + sourceCursor = source.getCursor(); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (!resizing) { + Component source = e.getComponent(); + source.setCursor(sourceCursor); + } + } + + @Override + public void mousePressed(MouseEvent e) { + // The mouseMoved event continually updates this variable + + if (direction == 0) + return; + + // Setup for resizing. All future dragging calculations are done based + // on the original bounds of the component and mouse pressed location. + + resizing = true; + + Component source = e.getComponent(); + pressed = e.getPoint(); + SwingUtilities.convertPointToScreen(pressed, source); + bounds = source.getBounds(); + + // Making sure autoscrolls is false will allow for smoother resizing + // of components + + if (source instanceof JComponent) { + JComponent jc = (JComponent) source; + autoscrolls = jc.getAutoscrolls(); + jc.setAutoscrolls(false); + } + } + + /** + * Restore the original state of the Component + */ + @Override + public void mouseReleased(MouseEvent e) { + resizing = false; + + Component source = e.getComponent(); + source.setCursor(sourceCursor); + + if (source instanceof JComponent) { + ((JComponent) source).setAutoscrolls(autoscrolls); + } + } + + /** + * Resize the component ensuring location and size is within the bounds of + * the parent container and that the size is within the minimum and maximum + * constraints. + * + * All calculations are done using the bounds of the component when the + * resizing started. + */ + @Override + public void mouseDragged(MouseEvent e) { + if (resizing == false) + return; + + Component source = e.getComponent(); + Point dragged = e.getPoint(); + SwingUtilities.convertPointToScreen(dragged, source); + + changeBounds(source, direction, bounds, pressed, dragged); + } + + protected void changeBounds(Component source, int direction, + Rectangle bounds, Point pressed, Point current) { + // Start with original locaton and size + + int x = bounds.x; + int y = bounds.y; + int width = bounds.width; + int height = bounds.height; + + // Resizing the West or North border affects the size and location + + if (WEST == (direction & WEST)) { + int drag = getDragDistance(pressed.x, current.x, snapSize.width); + int maximum = Math.min(width + x, maximumSize.width); + drag = getDragBounded(drag, snapSize.width, width, + minimumSize.width, maximum); + + x -= drag; + width += drag; + } + + if (NORTH == (direction & NORTH)) { + int drag = getDragDistance(pressed.y, current.y, snapSize.height); + int maximum = Math.min(height + y, maximumSize.height); + drag = getDragBounded(drag, snapSize.height, height, + minimumSize.height, maximum); + + y -= drag; + height += drag; + } + + // Resizing the East or South border only affects the size + + if (EAST == (direction & EAST)) { + int drag = getDragDistance(current.x, pressed.x, snapSize.width); + Dimension boundingSize = getBoundingSize(source); + int maximum = Math.min(boundingSize.width - x, maximumSize.width); + drag = getDragBounded(drag, snapSize.width, width, + minimumSize.width, maximum); + width += drag; + } + + if (SOUTH == (direction & SOUTH)) { + int drag = getDragDistance(current.y, pressed.y, snapSize.height); + Dimension boundingSize = getBoundingSize(source); + int maximum = Math.min(boundingSize.height - y, maximumSize.height); + drag = getDragBounded(drag, snapSize.height, height, + minimumSize.height, maximum); + height += drag; + } + + source.setBounds(x, y, width, height); + source.validate(); + } + + /* + * Determine how far the mouse has moved from where dragging started + */ + private int getDragDistance(int larger, int smaller, int snapSize) { + int halfway = snapSize / 2; + int drag = larger - smaller; + drag += (drag < 0) ? -halfway : halfway; + drag = (drag / snapSize) * snapSize; + + return drag; + } + + /* + * Adjust the drag value to be within the minimum and maximum range. + */ + private int getDragBounded(int drag, int snapSize, int dimension, + int minimum, int maximum) { + while (dimension + drag < minimum) + drag += snapSize; + + while (dimension + drag > maximum) + drag -= snapSize; + + return drag; + } + + /* + * Keep the size of the component within the bounds of its parent. + */ + private Dimension getBoundingSize(Component source) { + if (source instanceof Window) { + GraphicsEnvironment env = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + Rectangle bounds = env.getMaximumWindowBounds(); + return new Dimension(bounds.width, bounds.height); + } else { + return source.getParent().getSize(); + } + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/FragmentImpl.java b/plugin/jcapture/src/com/hammurapi/jcapture/FragmentImpl.java new file mode 100644 index 000000000..0d4b066af --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/FragmentImpl.java @@ -0,0 +1,28 @@ +package com.hammurapi.jcapture; + +import java.io.File; +import java.util.List; + +import com.hammurapi.jcapture.VideoEncoder.Fragment; + +class FragmentImpl implements Fragment { + + private File audio; + private List frames; + + FragmentImpl( List frames, File audio) { + this.audio = audio; + this.frames = frames; + } + + @Override + public List getFrames() { + return frames; + } + + @Override + public File getAudio() { + return audio; + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/FrameImpl.java b/plugin/jcapture/src/com/hammurapi/jcapture/FrameImpl.java new file mode 100644 index 000000000..69172c0f4 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/FrameImpl.java @@ -0,0 +1,57 @@ +package com.hammurapi.jcapture; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.List; + +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame; + +class FrameImpl implements Frame { + + private List shapes; + private Point mousePointer; + private Dimension size; + private boolean isActive; + + FrameImpl(List shapes, Point mousePointer, Dimension size, boolean isActive) { + super(); + this.shapes = shapes; + this.mousePointer = mousePointer; + this.size = size; + this.isActive = isActive; + } + + /** + * Merges frame before this frame into this frame by incorporating its shapes. + * This method is used for merging deleted frames. + * @param frame + */ + void merge(Frame frame) { + for (Shape shape: shapes) { + if (shape.getContent().coversEverything()) { + return; // No need in previous shapes. + } + } + shapes.addAll(0, frame.getShapes()); + } + + @Override + public List getShapes() { + return shapes; + } + + @Override + public Point getMousePointer() { + return mousePointer; + } + + @Override + public Dimension getSize() { + return size; + } + + @Override + public boolean isActive() { + return isActive; + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/GraphicsDeviceTranslucener.java b/plugin/jcapture/src/com/hammurapi/jcapture/GraphicsDeviceTranslucener.java new file mode 100644 index 000000000..36a0ec395 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/GraphicsDeviceTranslucener.java @@ -0,0 +1,21 @@ +package com.hammurapi.jcapture; + +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GraphicsDevice.WindowTranslucency; + +public class GraphicsDeviceTranslucener extends Translucener { + + @Override + protected void makeTranslucent(Frame frame) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + + //If translucent windows aren't supported, exit. + if (gd.isWindowTranslucencySupported(WindowTranslucency.TRANSLUCENT)) { + frame.setOpacity(0.7f); + } + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ImageToolTip.java b/plugin/jcapture/src/com/hammurapi/jcapture/ImageToolTip.java new file mode 100644 index 000000000..f083fbd99 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ImageToolTip.java @@ -0,0 +1,80 @@ +/* + * This file is an adapted example from javareference.com + * for more information visit, + * http://www.javareference.com + */ +package com.hammurapi.jcapture; + +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JToolTip; +import javax.swing.SwingUtilities; +import javax.swing.plaf.metal.MetalToolTipUI; + +/** + * This class extends JToolTip and set the UI to ImageToolTipUI. + * + * @author Rahul Sapkal(rahul@javareference.com) + */ +public class ImageToolTip extends JToolTip { + + /** + * This class extends MetalToolTipUI and provides customizes it to draw a + * given image on it. + * + * @author Rahul Sapkal(rahul@javareference.com) + */ + private class ImageToolTipUI extends MetalToolTipUI { + private Image m_image; + + public ImageToolTipUI(Image image) { + m_image = image; + } + + /** + * This method is overriden from the MetalToolTipUI to draw the given + * image and text + */ + public void paint(Graphics g, JComponent c) { + FontMetrics metrics = c.getFontMetrics(g.getFont()); + g.setColor(c.getForeground()); + + g.drawString(((ImageToolTip) c).text, 3, 15); + + g.drawImage(m_image, 3, metrics.getHeight() + 3, c); + } + + /** + * This method is overriden from the MetalToolTipUI to return the + * appropiate preferred size to size the ToolTip to show both the text + * and image. + */ + public Dimension getPreferredSize(JComponent c) { + FontMetrics metrics = c.getFontMetrics(c.getFont()); + String tipText = ((JToolTip) c).getTipText(); + if (tipText == null) { + tipText = ""; + } + + int width = SwingUtilities.computeStringWidth(metrics, tipText); + int height = metrics.getHeight() + m_image.getHeight(c) + 6; + + if (width < m_image.getWidth(c)) { + width = m_image.getWidth(c); + } + + return new Dimension(width, height); + } + } + + private String text; + + public ImageToolTip(String text, Image image) { + this.text = text; + setUI(new ImageToolTipUI(image)); + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/JCaptureApplet.java b/plugin/jcapture/src/com/hammurapi/jcapture/JCaptureApplet.java new file mode 100644 index 000000000..1627c6a4d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/JCaptureApplet.java @@ -0,0 +1,92 @@ +package com.hammurapi.jcapture; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.mime.MultipartEntity; +import org.apache.http.entity.mime.content.InputStreamBody; +import org.apache.http.entity.mime.content.StringBody; + +public class JCaptureApplet extends AbstractCaptureApplet { + + private static final String HTTPS_PREFIX = "https://"; + + protected HttpUriRequest createRequest(String fileName, InputStreamBody bin) throws Exception { + String uploadUrl = getParameter("uploadUrl"); + if (uploadUrl==null || uploadUrl.trim().length()==0) { + String host = getParameter("host"); + String dokuHost = host; + + if (dokuHost.toLowerCase().startsWith(HTTPS_PREFIX)) { + if (dokuHost.lastIndexOf(":")0) { + reqEntity.addPart("sectok", new StringBody(sectok)); + } + reqEntity.addPart("ow", new StringBody("1")); + + String opaque = getParameter("opaque"); + if (opaque!=null && opaque.trim().length()>0) { + reqEntity.addPart("opaque", new StringBody(opaque)); + } + + reqEntity.addPart("Filename", new StringBody(fileName)); + + int nsIdx = fileName.lastIndexOf(":"); + String namespace; + if (nsIdx==-1) { + namespace = ":"; + } else { + namespace = ":"+fileName.substring(0, nsIdx); + fileName = fileName.substring(nsIdx+1); + } + + if (namespace!=null) { + reqEntity.addPart("ns", new StringBody(namespace)); + } + + reqEntity.addPart("Filedata", bin); + + httppost.setEntity(reqEntity); + return httppost; + } + + String getDokuBase() throws DecoderException { + return new String(Hex.decodeHex(getParameter("dokuBase").toCharArray())); + } + + @Override + protected String bodyName(String fileName) { + return fileName.substring(fileName.lastIndexOf(":")+1); + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/MappedImage.java b/plugin/jcapture/src/com/hammurapi/jcapture/MappedImage.java new file mode 100644 index 000000000..6f7d401fe --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/MappedImage.java @@ -0,0 +1,121 @@ +package com.hammurapi.jcapture; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.util.zip.Adler32; + +import javax.imageio.ImageIO; + +/** + * Mapped image is softly kept in memory and also is written to a temporary file. + * If image reference is cleared by the garbage collector, the image is loaded from the file on demand. + * @author Pavel + * + */ +public class MappedImage { + + private Reference imageRef; + private Reference imageBytesRef; + private MappedByteBuffer buffer; + private int height; + private int width; + private String format; + private long checksum; + private int bytesLength; + + public MappedImage(final BufferedImage image, String format, FileChannel channel) throws IOException { + if (format==null) { + throw new NullPointerException("Format is null"); + } + + class HardReference extends SoftReference { + + HardReference(BufferedImage referent) { + super(referent); + } + + @Override + public BufferedImage get() { + return image; + } + + } + imageRef = channel==null ? new HardReference(image) : new SoftReference(image); + width = image.getWidth(); + height = image.getHeight(); + this.format = format; + if (channel!=null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(imageRef.get(), format, baos); + baos.close(); + byte[] imageBytes = baos.toByteArray(); + Adler32 adler = new Adler32(); + adler.update(imageBytes); + checksum = adler.getValue(); + bytesLength = imageBytes.length; + imageBytesRef = new SoftReference(imageBytes); + synchronized (channel) { + long position = channel.position(); + channel.write(ByteBuffer.wrap(imageBytes)); + buffer = channel.map(MapMode.READ_ONLY, position, imageBytes.length); + } + } + } + + public byte[] getImageBytes() throws IOException { + if (imageBytesRef==null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(imageRef.get(), format, baos); + return baos.toByteArray(); + } + byte[] ret = imageBytesRef.get(); + if (ret==null) { + buffer.load(); + buffer.rewind(); + ret = new byte[buffer.remaining()]; + buffer.get(ret); + if (bytesLength != ret.length) { + throw new IllegalStateException("Invalid image bytes length, expected "+bytesLength+", got "+ret.length); + } + + Adler32 adler = new Adler32(); + adler.update(ret); + if (checksum != adler.getValue()) { + throw new IllegalStateException("Invalid image bytes checksum"); + } + imageBytesRef = new SoftReference(ret); + } + return ret; + } + + /** + * Reads from reference, if reference was cleared, loads from the mapped buffer. + * @return + * @throws IOException + */ + public BufferedImage getImage() throws IOException { + BufferedImage ret = imageRef.get(); + if (ret==null) { + ret = ImageIO.read(new ByteArrayInputStream(getImageBytes())); + imageRef = new SoftReference(ret); + } + return ret; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/Movie.java b/plugin/jcapture/src/com/hammurapi/jcapture/Movie.java new file mode 100644 index 000000000..c7303e971 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/Movie.java @@ -0,0 +1,55 @@ +package com.hammurapi.jcapture; + +import java.awt.Dimension; +import java.io.Closeable; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.List; + +public class Movie implements Closeable { + + private float framesPerSecond; + private List fragments; + private Dimension frameDimension; + private Closeable imagesFileCloseable; + + public Movie(Dimension frameDimension, float framesPerSecond, List fragments, Closeable imagesFileCloseable) { + super(); + this.frameDimension = frameDimension; + this.framesPerSecond = framesPerSecond; + this.fragments = fragments; + this.imagesFileCloseable = imagesFileCloseable; + } + + public List getFragments() { + return fragments; + } + + public float getFramesPerSecond() { + return framesPerSecond; + } + + public Dimension getFrameDimension() { + return frameDimension; + } + + @Override + public String toString() { + int frames = 0; + for (VideoEncoder.Fragment f: fragments) { + frames+=f.getFrames().size(); + } + + long length = (long) (frames/framesPerSecond); + + return MessageFormat.format("{0,number,00}:{1,number,00}:{2,number,00}, {3} frames", length/3600, (length/60) % 60, length % 60, frames); + } + + @Override + public void close() throws IOException { + if (imagesFileCloseable!=null) { + imagesFileCloseable.close(); + } + } + +} \ No newline at end of file diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/MovieEditorDialog.java b/plugin/jcapture/src/com/hammurapi/jcapture/MovieEditorDialog.java new file mode 100644 index 000000000..297420a2d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/MovieEditorDialog.java @@ -0,0 +1,1124 @@ +package com.hammurapi.jcapture; + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JToolTip; +import javax.swing.ListSelectionModel; +import javax.swing.ProgressMonitor; +import javax.swing.SwingWorker; +import javax.swing.Timer; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableModel; + +import com.hammurapi.jcapture.ShapeImpl.ImageImpl; +import com.hammurapi.jcapture.VideoEncoder.Fragment; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.ImageReference; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.ShapeContent; + +public class MovieEditorDialog extends javax.swing.JDialog { + + private static final double DECIBELS_PER_PIXEL = 2.0; + private static final double NORMALIZED_LEVEL = 0.95; + private static final int AUDIO_CELL_HEIGHT = 50; + private static final int MEDIAN = AUDIO_CELL_HEIGHT/2; + int minCellDimension = 10; + int minToolTipImageDimension = 150; + + int splashIndex = -1; + + double coeff; + + private static Color INACTIVE_COLOR = new Color(230, 230, 230); + private static Color ACTIVE_COLOR = Color.white; + private static Color SELECTED_COLOR = new Color(0, 0, 255, 70); + private static Color FOCUSED_COLOR = new Color(0, 0, 255, 100); + private static Color PLAYING_COLOR = new Color(255, 0, 0, 100); + + private static Color SPLASH_COLOR = new Color(0, 255, 0, 127); + + private static Color SOUND_COLOR = new Color(0, 0, 127); + private static Color DELETED_SOUND_COLOR = new Color(100, 100, 100); + + private JButton saveButton; + private JPanel contentPanel; + private JScrollPane timeLineScrollPane; + private JCheckBox normalizeVolumeCheckBox; + private JPanel frameCanvas; + JTable timeLineTable; + private JButton discardButton; + private int focusColumn = 0; + private int playingColumn = -1; + private Image mouseImage; + private double maxVolume = -1; + FrameEntry[] frameEntries; + int cellWidth; + int cellHeight; + int toolTipImageWidth; + int toolTipImageHeight; + boolean hasAudio; + private Movie movie; + + int numChannels; + int validBits; + long sampleRate; + Timer[] playTimera = {null}; + private Executor backgroundProcessor; + private double inactivityInterval; + private String imageFormat; + + private class FrameEntry { + + // Not null for first frames in fragments indicating that + // Indicating that it's time to open a new audio file. + File audioFile; + + boolean mouseMoved; + + // Scaled samples for painting - not real ones. + // idx, {min, max} + double[] audioSamples; + + // Number of real samples falling to this frame. + int audioSamplesInFrame; + + boolean isDeleted; + + Reference toolTipImageRef; + + Reference frameImageRef; + + // row, selected, focus + private JPanel[][][] canvases = { + { {new FrameCellCanvas(false, false), new FrameCellCanvas(false, true)}, {new FrameCellCanvas(true, false), new FrameCellCanvas(true, true)} }, + { {new AudioCellCanvas(false, false), new AudioCellCanvas(false, true)}, {new AudioCellCanvas(true, false), new AudioCellCanvas(true, true)} } + }; + + class CellCanvas extends JPanel { + + boolean selected; + boolean hasFocus; + + CellCanvas(boolean selected, boolean hasFocus) { + this.selected = selected; + this.hasFocus = hasFocus; + } + + } + + class FrameCellCanvas extends CellCanvas { + + FrameCellCanvas(boolean selected, boolean hasFocus) { + super(selected, hasFocus); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + paintFrame(this, g, selected, hasFocus); + } + + } + + class AudioCellCanvas extends CellCanvas { + + AudioCellCanvas(boolean selected, boolean hasFocus) { + super(selected, hasFocus); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + paintAudio(this, g, selected, hasFocus); + } + + } + + int idx; + int delta; + + Frame frame; + + BufferedImage getToolTipImage() throws IOException { + BufferedImage ret = toolTipImageRef==null ? null : toolTipImageRef.get(); + if (ret == null) { + BufferedImage image = getImage(); + ret = new BufferedImage(toolTipImageWidth, toolTipImageHeight, image.getType()); + Graphics2D g = ret.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.drawImage(image, 0, 0, ret.getWidth(), ret.getHeight(), null); + g.dispose(); + toolTipImageRef = new SoftReference(ret); + } + return ret; + + } + + void paintFrame(FrameCellCanvas frameCellCanvas, Graphics g, boolean selected, boolean hasFocus) { + g.setColor(frame.isActive() ? ACTIVE_COLOR : INACTIVE_COLOR); + g.fillRect(0, 0, frameCellCanvas.getWidth(), frameCellCanvas.getHeight()); + + if (idx==splashIndex) { + g.setColor(SPLASH_COLOR); + g.fillRect(1, 1, frameCellCanvas.getWidth()-2, frameCellCanvas.getHeight()-2); + + } + + if (frame.getMousePointer()!=null) { + int mx = (int) (frame.getMousePointer().getX()*(frameCellCanvas.getWidth()-3)/frame.getSize().getWidth())+1; + int my = (int) (frame.getMousePointer().getY()*(frameCellCanvas.getHeight()-3)/frame.getSize().getHeight())+1; + g.setColor(mouseMoved ? Color.BLACK : Color.GRAY); + g.fillRect(mx, my, 2, 2); + } + + if (isDeleted) { + g.setColor(Color.RED); + g.drawLine(2, 2, frameCellCanvas.getWidth()-2, frameCellCanvas.getHeight()-2); + g.drawLine(frameCellCanvas.getWidth()-2, 2, 2, frameCellCanvas.getHeight()-2); + } + + decorate(frameCellCanvas, g, selected, hasFocus); + } + + void paintAudio(AudioCellCanvas audioCellCanvas, Graphics g, boolean selected, boolean hasFocus) { + g.setColor(frame.isActive() ? ACTIVE_COLOR : INACTIVE_COLOR); + g.fillRect(0, 0, audioCellCanvas.getWidth(), audioCellCanvas.getHeight()); + + if (audioSamples!=null) { + for (int i = 0; i0 && !coversEverything(startIdx)) { + --startIdx; + } + int deltaArea = 0; + ret = new BufferedImage(frame.getSize().width, frame.getSize().height, shapeImage(frameEntries[startIdx].frame.getShapes().get(0)).getType()); + Graphics2D g = ret.createGraphics(); + for (int i=startIdx; i<=idx; ++i) { + for (Shape shape: frameEntries[i].frame.getShapes()) { + BufferedImage si = shapeImage(shape); + g.drawImage(si, shape.getLocation().x, shape.getLocation().y, null); + if (i==idx) { + deltaArea+=si.getWidth()*si.getHeight(); + } + } + } + delta = (int) (100.0*deltaArea/(frame.getSize().width * frame.getSize().height)); + if (frame.getMousePointer()!=null) { + g.drawImage(mouseImage, frame.getMousePointer().x, frame.getMousePointer().y, null); + } + frameImageRef = new SoftReference(ret); + } + return ret; + } + + private BufferedImage shapeImage(Shape shape) throws IOException { + ShapeContent shapeContent = shape.getContent(); + if (shapeContent instanceof ImageReference) { + return ((ImageReference) shapeContent).getImage().getImage().getImage(); + } + return ((com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.Image) shapeContent).getImage().getImage(); + } + + boolean coversEverything(int entryIdx) { + for (Shape shape: frameEntries[entryIdx].frame.getShapes()) { + if (shape.getContent().coversEverything()) { + return true; + } + } + return false; + } + + public Component getCellRendererComponent(int row, boolean isSelected, boolean hasFocus) { + return canvases[row][isSelected ? 1 : 0][hasFocus ? 1 : 0]; + } + + } + + public MovieEditorDialog( + final JFrame frame, + final Movie movie, + final Executor backgroundProcessor, + double inactivityInterval, + String imageFormat) { + + super(frame, "Movie editor ("+movie+")"); + frame.setAlwaysOnTop(false); + frame.setVisible(false); + + this.movie = movie; + this.backgroundProcessor = backgroundProcessor; + this.inactivityInterval = inactivityInterval; + this.imageFormat = imageFormat; + + setModal(true); + setIconImage(frame.getIconImage()); + + mouseImage = Toolkit.getDefaultToolkit().getImage(getClass().getResource("mouse.png")); + + double aspectRatio = (double) movie.getFrameDimension().getWidth()/(double) movie.getFrameDimension().getHeight(); + + if (aspectRatio>1) { + cellHeight = minCellDimension; + cellWidth = (int) Math.round(aspectRatio*cellHeight); + + toolTipImageHeight = minToolTipImageDimension; + toolTipImageWidth = (int) Math.round(aspectRatio*toolTipImageHeight); + } else { + cellWidth = minCellDimension; + cellHeight = (int) Math.round((double) cellWidth/aspectRatio); + + toolTipImageWidth = minToolTipImageDimension; + toolTipImageHeight = (int) Math.round((double) toolTipImageWidth/aspectRatio); + } + + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + int confirmed = JOptionPane.showConfirmDialog(null, + "Are you sure you want to exit and discard the movie?", "User Confirmation", + JOptionPane.YES_NO_OPTION); + if (confirmed == JOptionPane.YES_OPTION) { + dispose(); + getOwner().setVisible(false); + } + } + }); + + SwingWorker loader = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + int totalFrames = 0; + for (Fragment fr: movie.getFragments()) { + if (fr.getAudio()!=null) { + hasAudio = true; + } + totalFrames+=fr.getFrames().size(); + } + + ProgressMonitor progressMonitor = new ProgressMonitor(frame, "Loading frames", "Loading movie frames", 0, totalFrames); + + try { + frameEntries = new FrameEntry[totalFrames]; + int idx = 0; + double audioSamplesPerFrame = -1; + Point prevMouse = null; + for (Fragment fr: movie.getFragments()) { + WavFile wavFile = fr.getAudio()==null ? null : WavFile.openWavFile(fr.getAudio()); + if (wavFile!=null) { + audioSamplesPerFrame = wavFile.getSampleRate()/movie.getFramesPerSecond(); + numChannels = wavFile.getNumChannels(); + validBits = wavFile.getValidBits(); + sampleRate = wavFile.getSampleRate(); + } + int audioFramesRead = 0; + int framePosition = 0; + for (Frame frm: fr.getFrames()) { + if (progressMonitor.isCanceled()) { + return false; + } + frameEntries[idx] = new FrameEntry(); + frameEntries[idx].frame = frm; + frameEntries[idx].idx = idx; + if (frm.getMousePointer()!=null) { + frameEntries[idx].mouseMoved = !frm.getMousePointer().equals(prevMouse); + } + prevMouse = frm.getMousePointer(); + + if (framePosition == 0) { + frameEntries[idx].audioFile = fr.getAudio(); + } + + if (wavFile!=null && wavFile.getFramesRemaining()>0) { + frameEntries[idx].audioSamplesInFrame = (int) ((framePosition+1)*audioSamplesPerFrame-audioFramesRead); + frameEntries[idx].audioSamples = new double[cellWidth]; + double[][] sampleBuffer = new double[wavFile.getNumChannels()][frameEntries[idx].audioSamplesInFrame]; + frameEntries[idx].audioSamplesInFrame=wavFile.readFrames(sampleBuffer, frameEntries[idx].audioSamplesInFrame); + audioFramesRead+=frameEntries[idx].audioSamplesInFrame; + for (int i=0; i encoder = new SwingWorker() { + + @Override + protected Movie doInBackground() throws Exception { + ProgressMonitor progressMonitor = new ProgressMonitor(MovieEditorDialog.this, "Saving movie", "Composing movie", 0, frameEntries.length); + + List newFrames = new ArrayList(); + + if (splashIndex!=-1) { + newFrames.add(new FrameImpl( + Collections.singletonList((Shape) new ShapeImpl(new Point(0,0), new ImageImpl(new MappedImage(frameEntries[splashIndex].getImage(), imageFormat, null), true))) , + frameEntries[splashIndex].frame.getMousePointer(), + frameEntries[splashIndex].frame.getSize(), + false)); + } + + File newAudio = hasAudio ? File.createTempFile("jCaptureAudioSink", ".wav") : null; + + long numFrames=0; + for (FrameEntry fe: frameEntries) { + if (!fe.isDeleted) { + numFrames+=fe.audioSamplesInFrame; + } + } + WavFile newWavFile = newAudio==null ? null : WavFile.newWavFile(newAudio, numChannels, numFrames, validBits, sampleRate); + + File currentAudio = null; + WavFile currentWav = null; + + for (int i=0; i0 && !frameEntries[i].isDeleted) { + // Normalization + for (double[] ch: buf) { + for (int j=0; j0 && !frameEntries[i].isDeleted) { + newWavFile.writeFrames(buf, read); + } + } + } + + if (frameEntries[i].isDeleted) { + if (i=0 && timeLineTable.isColumnSelected(i); --i) { + range[0] = i; + } + + if (range[0]==range[1]) { + range[1]=frameEntries.length-1; + } + + playingColumn = range[0]; + + if (hasAudio) { + try { + backgroundProcessor.execute(new SoundPlayer(range[0], range[1])); + } catch (Exception ex) { + ex.printStackTrace(); + JOptionPane.showMessageDialog( + MovieEditorDialog.this, + ex.toString(), + "Audio problem", + JOptionPane.ERROR_MESSAGE); + + } + } + + playTimera[0] = new Timer((int) ((double) 1000/movie.getFramesPerSecond()), new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + while (frameEntries[playingColumn].isDeleted) { + ++playingColumn; + if (playingColumn>range[1]) { + ((Timer) e.getSource()).stop(); + return; + } + } + + Rectangle visibleRect = timeLineTable.getVisibleRect(); + Rectangle playingRect = timeLineTable.getCellRect(0, playingColumn, true); + if (!visibleRect.contains(playingRect)) { + Rectangle scrollTo = new Rectangle(playingRect.x, playingRect.width, visibleRect.width-1, visibleRect.height-1); + timeLineTable.scrollRectToVisible(scrollTo); + } + + frameCanvas.repaint(); + timeLineTable.repaint(); + + ++playingColumn; + if (playingColumn>range[1]) { + ((Timer) e.getSource()).stop(); + return; + } + } + + + }) { + @Override + public void stop() { + super.stop(); + playingColumn=-1; + timeLineTable.scrollRectToVisible(timeLineTable.getCellRect(0, focusColumn, true)); + frameCanvas.repaint(); + timeLineTable.repaint(); + playTimera[0] = null; + } + }; + + playTimera[0].start(); + synchronized (playTimera) { + playTimera.notifyAll(); + } + } + }; + playMenuItem.setAction(playAction); + + popup.add(playMenuItem); + + timeLineTable.setComponentPopupMenu(popup ); + + timeLineTable.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + if (playTimera[0]!=null) { + playTimera[0].stop(); + } + + if (e.getClickCount()==2) { + int hitColumnIndex = timeLineTable.columnAtPoint(e.getPoint()); + if (hitColumnIndex!=-1) { + frameEntries[hitColumnIndex].isDeleted=!frameEntries[hitColumnIndex].isDeleted; + timeLineTable.repaint(); + } + } + } + }); + + timeLineTable.setToolTipText("Movie timeline"); + timeLineScrollPane.setViewportView(timeLineTable); + timeLineTable.setModel(timeLineTableModel); + timeLineTable.setRowHeight(0, cellHeight+timeLineTable.getRowMargin()*2); + timeLineTable.setRowHeight(1, AUDIO_CELL_HEIGHT+timeLineTable.getRowMargin()*2); + for (int i=0; i 0) { + break; + } + } catch (NumberFormatException nfe) { + // NOP - loop + } + msg = "Invalid double value for inactivity interval: "+newVal+", enter valid value"; + } + int inactivityInFrames = (int) (inactivityInterval*movie.getFramesPerSecond()); + int lastActivity = -inactivityInFrames-1; + for (int idx: timeLineTable.getSelectedColumns()) { + if (!frameEntries[idx].isDeleted && frameEntries[idx].frame.isActive()) { + lastActivity = idx; + } else if (idx - lastActivity > inactivityInFrames && !frameEntries[idx].frame.isActive()) { + frameEntries[idx].isDeleted = true; + } + } + timeLineTable.repaint(); + } + }; + + removeInactivityMenuItem.setAction(deleteFrameAction); + popup.add(removeInactivityMenuItem); + } + + private class SoundPlayer implements Runnable { + + private final int BUFFER_SIZE; + private AudioInputStream audioStream; + private SourceDataLine sourceLine; + private File audioFile; + + public SoundPlayer(int start, int end) throws Exception { + + audioFile = hasAudio ? File.createTempFile("jCaptureRangeAudio", ".wav") : null; + + BUFFER_SIZE = (int) ((double) numChannels*sampleRate*validBits/(movie.getFramesPerSecond()*8)); // 1 frame buffer. + + long numFrames=0; + for (int i = start; i<=end; ++i) { + if (!frameEntries[i].isDeleted) { + numFrames+=frameEntries[i].audioSamplesInFrame; + } + } + WavFile newWavFile = audioFile==null ? null : WavFile.newWavFile(audioFile, numChannels, numFrames, validBits, sampleRate); + + File currentAudio = null; + WavFile currentWav = null; + + for (int i=0; i<=end; ++i) { + if (frameEntries[i].audioFile!=null) { + if (currentWav!=null) { + currentWav.close(); + } + + currentAudio = frameEntries[i].audioFile; + currentWav = WavFile.openWavFile(currentAudio); + } + + if (currentWav!=null) { + if (normalizeVolumeCheckBox!=null && normalizeVolumeCheckBox.isSelected()) { + double[][] buf = new double[numChannels][frameEntries[i].audioSamplesInFrame]; + int read = currentWav.readFrames(buf, frameEntries[i].audioSamplesInFrame); + if (read>0 && i>=start && !frameEntries[i].isDeleted) { + // Normalization + for (double[] ch: buf) { + for (int j=0; j0 && i>=start && !frameEntries[i].isDeleted) { + newWavFile.writeFrames(buf, read); + } + } + } + } + + if (currentWav!=null) { + currentWav.close(); + } + if (newWavFile!=null) { + newWavFile.close(); + } + + if (audioFile!=null) { + audioStream = AudioSystem.getAudioInputStream(audioFile); + AudioFormat audioFormat = audioStream.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + sourceLine = (SourceDataLine) AudioSystem.getLine(info); + sourceLine.open(audioFormat); + } + } + + @Override + public void run() { + try { + sourceLine.start(); + synchronized (playTimera) { + if (playTimera[0] == null) { + playTimera.wait(100); + } + } + try { + byte[] buf = new byte[BUFFER_SIZE]; + int l; + while (playTimera[0]!=null && (l=audioStream.read(buf))!=-1) { + sourceLine.write(buf, 0, l); + } + } finally { + audioStream.close(); + sourceLine.drain(); + sourceLine.close(); + if (!audioFile.delete()) { + audioFile.deleteOnExit(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/RecordingControlsFrame.java b/plugin/jcapture/src/com/hammurapi/jcapture/RecordingControlsFrame.java new file mode 100644 index 000000000..2196ce30d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/RecordingControlsFrame.java @@ -0,0 +1,321 @@ +package com.hammurapi.jcapture; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingWorker; +import javax.swing.WindowConstants; +import javax.swing.border.LineBorder; + +import netscape.javascript.JSObject; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; + +public class RecordingControlsFrame extends javax.swing.JFrame { + private static final String RESUME_TEXT = "Resume"; + private static final String PAUSE_TEXT = "Pause"; + private JButton pauseButton; + private JButton cancelButton; + private JButton stopButton; + private ScreenRecorder screenRecorder; + private CaptureFrame captureFrame; + + public RecordingControlsFrame(final CaptureFrame captureFrame, final JFrame[] borderFrames) { + super("jCapture recording"); + setIconImage(captureFrame.getIconImage()); + this.captureFrame = captureFrame; + + setUndecorated(true); + setAlwaysOnTop(!getBounds().intersects(captureFrame.getBounds())); + + addComponentListener(new ComponentListener() { + + @Override + public void componentShown(ComponentEvent e) { + for (JFrame bf: borderFrames) { + if (bf!=null) { + bf.setVisible(true); + } + } + } + + @Override + public void componentResized(ComponentEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void componentMoved(ComponentEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void componentHidden(ComponentEvent e) { + for (JFrame bf: borderFrames) { + if (bf!=null) { + bf.setVisible(false); + } + } + } + }); + + JPanel contentPanel = new JPanel(); + contentPanel.setBorder(new LineBorder(new java.awt.Color(0, 0, 0), 1, false)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + + GridBagLayout thisLayout = new GridBagLayout(); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + thisLayout.rowWeights = new double[] { 0.0, 0.1, 0.0 }; + thisLayout.rowHeights = new int[] { 7, 7, 7 }; + thisLayout.columnWeights = new double[] { 0.0, 0.1, 0.0, 0.1, 0.0, 0.1, 0.0 }; + thisLayout.columnWidths = new int[] { 7, 20, 7, 20, 7, 7, 7 }; + contentPanel.setLayout(thisLayout); + + pauseButton = new JButton(); + contentPanel.add(pauseButton, new GridBagConstraints(1, 1, 1, 1, 0.0, + 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + pauseButton.setText(PAUSE_TEXT); + pauseButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + try { + if (PAUSE_TEXT.equals(pauseButton.getText())) { + screenRecorder.stop(); + pauseButton.setText(RESUME_TEXT); + } else { + screenRecorder.start(); + pauseButton.setText(PAUSE_TEXT); + } + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(RecordingControlsFrame.this, + e.toString(), "Error pausing/resuming recording", + JOptionPane.ERROR_MESSAGE); + } + } + }); + + stopButton = new JButton(); + contentPanel.add(stopButton, new GridBagConstraints(3, 1, 1, 1, 0.0, + 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + stopButton.setText("Stop"); + stopButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + + pauseButton.setEnabled(false); + stopButton.setEnabled(false); + cancelButton.setEnabled(false); + + SwingWorker task = new SwingWorker() { + + @Override + protected Movie doInBackground() throws Exception { + screenRecorder.stop(); + return screenRecorder.getMovie(); + } + + @Override + protected void done() { + try { + final Movie movie = get(); + if (movie!=null) { + if (JOptionPane.showConfirmDialog(RecordingControlsFrame.this, "Would you like to edit the movie before uploading?", "Edit movie?", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION) { + new MovieEditorDialog( + RecordingControlsFrame.this, + movie, + captureFrame.getCaptureConfig().getBackgroundProcessor(), + captureFrame.getCaptureConfig().getInactivityInterval(), + captureFrame.getCaptureConfig().getImageFormat()); + } else { + uploadMovie(movie); + } + } else { + JOptionPane.showMessageDialog( + RecordingControlsFrame.this, + "Recording discarded", + "Saving recording", + JOptionPane.INFORMATION_MESSAGE); + RecordingControlsFrame.this.setVisible(false); + } + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog( + RecordingControlsFrame.this, e.toString(), + "Error saving recording", + JOptionPane.ERROR_MESSAGE); + RecordingControlsFrame.this.setVisible(false); + } + } + + }; + + task.execute(); + + } + }); + + cancelButton = new JButton(); + contentPanel.add(cancelButton, new GridBagConstraints(5, 1, 1, 1, 0.0, + 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 0), 0, 0)); + cancelButton.setText("Cancel"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (JOptionPane.showConfirmDialog(RecordingControlsFrame.this, "Are you sure you want to discard the recording?", "Confirm discarding movie", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION) {; + try { + screenRecorder.stop(); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(RecordingControlsFrame.this, + e.toString(), "Error cancelling recording", + JOptionPane.ERROR_MESSAGE); + } finally { + RecordingControlsFrame.this.setVisible(false); + captureFrame.setVisible(true); + } + } + } + }); + + pack(); + this.setSize(301, 40); + captureFrame.getCaptureConfig().setParentComponent(this); + try { + screenRecorder = new ScreenRecorder(captureFrame.getCaptureConfig(), captureFrame.getApplet()); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, e.toString(), "Error starting recording", JOptionPane.ERROR_MESSAGE); + setVisible(false); + } + + } + + /** + * Asks for file name and uploads the movie. + * @param movie + */ + void uploadMovie(final Movie movie) { + try { + if (movie!=null) { + + final String fileName = JOptionPane.showInputDialog( + RecordingControlsFrame.this, + "Upload as", + captureFrame.getApplet().getParameter("pageName")+ + "-recording-"+ + captureFrame.getDatePrefix()+ + "-" + captureFrame.nextCounter() +"."+captureFrame.getCaptureConfig().getEncoder().getFileExtension()); + + if (fileName!=null) { + // Uploading + SwingWorker task = new SwingWorker() { + + @Override + protected Dimension doInBackground() throws Exception { + + File savedTo = null; + + try { + // encode and upload + File tmpFile = File.createTempFile("jCaptureMovie", "."+captureFrame.getCaptureConfig().getEncoder().getFileExtension()); + FileOutputStream out = new FileOutputStream(tmpFile); + Dimension dimension = captureFrame.getCaptureConfig().getEncoder().encode(captureFrame.getCaptureConfig(), movie, out); + if (dimension==null) { + return null; + } + out.close(); + savedTo = tmpFile; + + HttpResponse iResponse = captureFrame.getApplet().post( + RecordingControlsFrame.this, + new FileInputStream(tmpFile), + tmpFile.length(), + fileName, + "application/octet-stream"); + + if (iResponse!=null) { + System.out.println("Response status line: "+iResponse.getStatusLine()); + if (iResponse.getStatusLine().getStatusCode()!=HttpStatus.SC_OK) { + errorMessage = iResponse.getStatusLine(); + errorTitle = "Error saving recording"; + return null; + } + } + if (!tmpFile.delete()) { + tmpFile.deleteOnExit(); + } + return dimension; + } catch (Error e) { + errorMessage=e.toString(); + if (savedTo!=null) { + errorMessage=errorMessage + ",\n recording was saved to "+savedTo.getAbsolutePath(); + } + errorTitle = "Upload error"; + e.printStackTrace(); + return null; + } + } + + private Object errorMessage; + private String errorTitle; + + protected void done() { + try { + Dimension dimension = get(); + if (dimension!=null) { + JSObject window = JSObject.getWindow(captureFrame.getApplet()); + String toEval = "insertAtCarret('"+captureFrame.getApplet().getParameter("edid")+"','{{:"+fileName+"?"+dimension.width+"x"+dimension.height+"|}}')"; + System.out.println("Evaluating: "+toEval); + window.eval(toEval); + } else { + JOptionPane.showMessageDialog( + RecordingControlsFrame.this, + errorMessage, + errorTitle, + JOptionPane.ERROR_MESSAGE); + } + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog( + RecordingControlsFrame.this, + e.toString(), + "Exception", + JOptionPane.ERROR_MESSAGE); + } + }; + + }; + + task.execute(); + } + } else { + JOptionPane.showMessageDialog( + RecordingControlsFrame.this, + "Recording discarded", + "Saving recording", + JOptionPane.INFORMATION_MESSAGE); + } + } finally { + RecordingControlsFrame.this.setVisible(false); + } + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/Region.java b/plugin/jcapture/src/com/hammurapi/jcapture/Region.java new file mode 100644 index 000000000..b5bc0a05d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/Region.java @@ -0,0 +1,221 @@ +package com.hammurapi.jcapture; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.channels.FileChannel; + +public class Region extends Rectangle { + + private static final int TRANSPARENT_COLOR = new Color(0,0,0,0).getRGB(); + + private BufferedImage master; + private BufferedImage prev; + private int grabRange; + private MappedImage image; + private boolean transparency; + + public Region(BufferedImage master, String format, FileChannel channel, BufferedImage prev, boolean transparency, int x, int y, int grabRange) { + this.master = master; + this.format = format; + this.channel = channel; + this.prev = prev; + this.transparency = transparency; + this.grabRange = grabRange; + + setBounds(x-grabRange, y-grabRange, grabRange*2+1, grabRange*2+1); + } + + /** + * Special case when region covers the whole image. + * @param master + * @param x + * @param y + * @param grabRange + * @throws IOException + */ + public Region(MappedImage master) throws IOException { + this.image = master; + this.grabRange = 0; + imageLocation = new Point(0,0); + coversEverything = true; + + setBounds(0,0,master.getWidth(),master.getHeight()); + BufferedImage img = master.getImage(); + for (int sx=0, sw=master.getWidth(); sx0) { + imageWidth-=widthDelta; + } + + int imageHeight = height; + int heightDelta = imageHeight+imageLocation.y - master.getHeight(); + if (heightDelta>0) { + imageHeight-=heightDelta; + } + + BufferedImage bImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + for (int x=0; x0) { + Thread.sleep(toSleep); + } + + screenShot = config.createScreenShot(screenShot, imagesChannel); + if (first==null) { + first = screenShot; + } + screenshots.add(config.submit(screenShot)); + } + + System.out.println("Captured "+screenshots.size()+" screenshots"); + } + + } + + public Fragment() throws Exception { + if (targetDataLine!=null) { + audioSink = File.createTempFile("jCaptureAudioSink", ".wav"); + targetDataLine.start(); + audioRecordingThread = new AudioRecordingThread(); + audioRecordingThread.start(); + } + + screenCapturingThread = new ScreenCapturingThread(); + screenCapturingThread.start(); + } + + File audioSink; + List> screenshots = new ArrayList>(); + + AudioRecordingThread audioRecordingThread; + ScreenCapturingThread screenCapturingThread; + + volatile boolean isDone; + + void stop() throws Exception { + if (targetDataLine!=null) { + targetDataLine.stop(); + } + isDone = true; + if (audioRecordingThread!=null) { + audioRecordingThread.join(); + } + screenCapturingThread.join(); + if (screenCapturingThread.getException()!=null) { + throw screenCapturingThread.getException(); + } + if (audioRecordingThread!=null && audioRecordingThread.getException()!=null) { + throw audioRecordingThread.getException(); + } + } + + } + + LinkedList fragments = new LinkedList(); + private FileChannel imagesChannel; + + public ScreenRecorder(CaptureConfig config, AbstractCaptureApplet applet) throws Exception { + this.config = config; + final File imagesFile = File.createTempFile("jCaptureImages", ".tmp"); + imagesFile.deleteOnExit(); + final RandomAccessFile raf = new RandomAccessFile(imagesFile, "rw"); + this.imagesChannel = raf.getChannel(); + + imagesFileCloseable = new Closeable() { + + @Override + public void close() throws IOException { + imagesChannel.close(); + raf.close(); + if (!imagesFile.delete()) { + imagesFile.deleteOnExit(); + } + } + + }; + + applet.addCloseable(imagesFileCloseable); + + if (config.isSound()) { + DataLine.Info info = new DataLine.Info(TargetDataLine.class, config.getAudioFormat()); + + Mixer mixer = null; + Mixer firstMixer = null; + for (Mixer.Info mi: AudioSystem.getMixerInfo()) { + Mixer mx = AudioSystem.getMixer(mi); + if (mx.isLineSupported(info)) { + if (firstMixer==null) { + firstMixer = mx; + } + if (config.getMixerName()==null || mi.getName().equals(config.getMixerName())) { + mixer = mx; + break; + } + } + } + + if (mixer==null) { + mixer = firstMixer; + } + + if (mixer!=null) { + targetDataLine = (TargetDataLine) mixer.getLine(info); + targetDataLine.open(config.getAudioFormat()); + } + } + + frameLength = (long) (1000.0/config.getFramesPerSecond()); + + start(); + } + + public synchronized void start() throws Exception { + fragments.add(new Fragment()); + } + + public void stop() throws Exception { + fragments.getLast().stop(); + } + + /** + * Recording is discarded if saveTo is null + * @param saveTo + * @return Movie size in pixels or null if saving was cancelled. + * @throws IOException + * @throws DataFormatException + */ + public Movie getMovie() throws Exception { + stop(); + + if (targetDataLine!=null) { + targetDataLine.close(); + } + + int totalWork = 3; + for (Fragment f: fragments) { + totalWork+=f.screenshots.size()+1; + } + + Map imageCache = new IdentityHashMap(); + + Dimension frameDimension = null; + + ProgressMonitor progressMonitor = new ProgressMonitor(config.getParentComponent(), "Encoding video", "Preparing frames", 0, totalWork+4); + try { + int progressCounter = 0; + + //In frames + int inactivityInterval = config.isRemoveInactivity() && !config.isSound() ? (int) (1000.0 * config.getInactivityInterval() / frameLength) : -1; + float fps = -1; + final List fragmentCollector = new ArrayList(); + for (Fragment fragment: fragments) { + if (progressMonitor.isCanceled()) { + return null; + } + + if (fps<0) { + fps = config.isSound() ? fragment.getActualFps() : config.getSpeedScale()*fragment.getActualFps(); + } + + progressMonitor.setProgress(++progressCounter); + + int lastActivity = -1; + List framesCollector = new ArrayList(); + for (Future sf: fragment.screenshots) { + + if (progressMonitor.isCanceled()) { + return null; + } + + ScreenShot screenShot = sf.get(); + + if (inactivityInterval<0 || screenShot.isActive() || screenShot.getSecNo()-lastActivity frameShapes = new ArrayList(); + for (Region region: screenShot.getRegions()) { + ShapeContent content; + if (region.getMasterImageRegion()==null) { + content = new ShapeImpl.ImageImpl(region.getImage(), region.coversEverything()); + imageCache.put(region, (Image) content); + if (frameDimension==null && region.coversEverything()) { + frameDimension = region.getSize(); + } + } else { + content = new ShapeImpl.ImageReferenceImpl(imageCache.get(region.getMasterImageRegion())); + } + frameShapes.add(new ShapeImpl(region.getImageLocation(), content)); + } + framesCollector.add(new FrameImpl(frameShapes, screenShot.getMousePosition(), screenShot.getSize(), screenShot.isActive())); + } else { + progressMonitor.setProgress(++progressCounter); // Skipping frame, report progress here. + } + + if (screenShot.isActive()) { + lastActivity = screenShot.getSecNo(); + } + + progressMonitor.setProgress(++progressCounter); + } + + fragmentCollector.add(new FragmentImpl(Collections.unmodifiableList(framesCollector), fragment.audioSink)); + } + + return new Movie(frameDimension, fps, fragmentCollector, imagesFileCloseable); + } finally { + progressMonitor.close(); + } + } + + private static abstract class SafeThread extends Thread { + private Exception exception; + + public SafeThread(String name) { + super(name); + } + + @Override + public void run() { + try { + runInternal(); + } catch (Exception e) { + this.exception = e; + e.printStackTrace(); + } + } + + protected abstract void runInternal() throws Exception; + + public Exception getException() { + return exception; + } + } + + long frameLength; + + + private TargetDataLine targetDataLine; + +} \ No newline at end of file diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ScreenShot.java b/plugin/jcapture/src/com/hammurapi/jcapture/ScreenShot.java new file mode 100644 index 000000000..080c127b0 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ScreenShot.java @@ -0,0 +1,251 @@ +package com.hammurapi.jcapture; + +import java.awt.AlphaComposite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.Callable; + +import javax.imageio.ImageIO; + +public class ScreenShot implements Callable { + + private final ScreenShot prev; + private final int secNo; + private ScreenShot next; + final private long timeStamp; + private int grabRange; + private boolean transparency; + private MappedImage image; + private Point mousePosition; + private double scale; + private boolean border; + private Dimension size; + private FileChannel imageChannel; + private String imageFormat; + + public ScreenShot( + BufferedImage image, + Point mousePosition, + ScreenShot prev, + long timeStamp, + int grabRange, + boolean transparency, + boolean border, + double scale, + FileChannel imageChannel, + String imageFormat) throws IOException { + + this.image = new MappedImage(image, imageFormat, imageChannel); + this.mousePosition = mousePosition; + this.prev = prev; + if (prev==null) { + secNo=0; + } else { + prev.next = this; + secNo = prev.secNo+1; + } + this.timeStamp = timeStamp; + this.grabRange = grabRange; + this.transparency = transparency; + this.scale = scale; + this.border = border; + this.imageChannel = imageChannel; + this.imageFormat = imageFormat; + } + + public Point getMousePosition() { + return mousePosition; + } + + /** + * Calculates actual FPS. + * @return + */ + public float getFramesPerSecond() { + long start = timeStamp; + long end = 0; + int length = 0; + for (ScreenShot sibling = next; sibling!=null; sibling=sibling.next) { + ++length; + end = sibling.timeStamp; + } + if (length==0) { + return -1; // No way to tell. + } + return (float) (length * 1000.0)/(end - start); + } + + private List regions; + + private long totalPixels; + private long differentPixels; + + public double getDiffLevel() { + return (double) differentPixels/(double) totalPixels; + } + + /** + * If images are different more than diffThreshold, then the + * entire screenshot shall be taken. + */ + private double diffThreshold = 0.7; + + /** + * Performs processing and returns self. + * Screenshot is structured as Callable to simplify live processing in a background thread. + */ + @Override + public ScreenShot call() throws Exception { + BufferedImage img = image.getImage(); + // No petty scaling. + if (scale<0.99 || scale > 1.01) { + BufferedImage scaled = new BufferedImage((int) (img.getWidth()*scale), (int) (img.getHeight()*scale), img.getType()); + Graphics2D g = scaled.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.drawImage(img, 0, 0, scaled.getWidth(), scaled.getHeight(), null); + g.dispose(); + img = scaled; + + if (mousePosition!=null) { + mousePosition = new Point((int) (mousePosition.x*scale), (int) (mousePosition.y*scale)); + } + } + + if (border) { + Graphics2D ssg = img.createGraphics(); + ssg.setColor(java.awt.Color.GRAY); + ssg.drawRect(0, 0, img.getWidth()-1, img.getHeight()-1); + } + + size = new Dimension(image.getWidth(), image.getHeight()); + + regions = new ArrayList(); + if (prev==null) { + regions.add(new Region(image)); + } else { + BufferedImage pimg = prev.image.getImage(); + for (int x=0, w=img.getWidth(); xdiffThreshold) { + regions.clear(); + regions.add(new Region(image)); + } else { + // Merging adjacent regions + for (int i=0; i lit = regions.listIterator(i+1); + Region master = regions.get(i); + while (lit.hasNext()) { + if (master.merge(lit.next())) { + lit.remove(); + } + } + } + + for (Region region: regions) { + region.grabImage(); + } + } + + // Eligible for garbage collection + if (prev!=null) { + prev.image=null; + } + } + + // De-dup + ListIterator oit = regions.listIterator(); + R: while (oit.hasNext()) { + Region or = oit.next(); + + if (oit.hasPrevious()) { + ListIterator iit = regions.listIterator(oit.previousIndex()); + while (iit.hasPrevious()) { + if (or.dedup(iit.previous())) { + continue R; + } + } + } + + for (ScreenShot sibling=prev; sibling!=null; sibling=sibling.prev) { + for (Region sr: sibling.regions) { + if (or.dedup(sr)) { + continue R; + } + } + } + } + return this; + } + + public void dump(File dir, String imageFormat) throws IOException { + for (int i=0; i getRegions() { + return regions; + } + + public int getSecNo() { + return secNo; + } + + public boolean isActive() { + if (!regions.isEmpty()) { + return true; + } + if (mousePosition==null) { + if (prev==null) { + return false; + } + if (prev.getMousePosition()!=null) { + return true; + } + return false; + } + + if (prev==null) { + return true; + } + if (!mousePosition.equals(prev.getMousePosition())) { + return true; + } + return false; + } + + public Dimension getSize() { + return size; + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/ShapeImpl.java b/plugin/jcapture/src/com/hammurapi/jcapture/ShapeImpl.java new file mode 100644 index 000000000..ca3fda48d --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/ShapeImpl.java @@ -0,0 +1,75 @@ +package com.hammurapi.jcapture; + +import java.awt.Point; + +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape; + +class ShapeImpl implements Shape { + + static class ImageImpl implements Image { + + MappedImage image; + private boolean coversEverything; + + ImageImpl(MappedImage image, boolean coversEverything) { + super(); + this.image = image; + this.coversEverything = coversEverything; + } + + @Override + public boolean coversEverything() { + return coversEverything; + } + + @Override + public MappedImage getImage() { + return image; + } + + } + + static class ImageReferenceImpl implements ImageReference { + + private Image image; + + ImageReferenceImpl(Image image) { + super(); + if (image==null) { + throw new NullPointerException(); + } + this.image = image; + } + + @Override + public boolean coversEverything() { + return image.coversEverything(); + } + + @Override + public Image getImage() { + return image; + } + + } + + private Point location; + private ShapeContent content; + + ShapeImpl(Point location, ShapeContent content) { + super(); + this.location = location; + this.content = content; + } + + @Override + public Point getLocation() { + return location; + } + + @Override + public ShapeContent getContent() { + return content; + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/SwfEncoder.java b/plugin/jcapture/src/com/hammurapi/jcapture/SwfEncoder.java new file mode 100644 index 000000000..88e59a868 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/SwfEncoder.java @@ -0,0 +1,353 @@ +package com.hammurapi.jcapture; + +import java.awt.AlphaComposite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.imageio.ImageIO; +import javax.swing.JOptionPane; +import javax.swing.ProgressMonitor; + +import com.flagstone.transform.Background; +import com.flagstone.transform.DefineTag; +import com.flagstone.transform.DoAction; +import com.flagstone.transform.Movie; +import com.flagstone.transform.MovieHeader; +import com.flagstone.transform.MovieTag; +import com.flagstone.transform.Place2; +import com.flagstone.transform.Remove; +import com.flagstone.transform.Remove2; +import com.flagstone.transform.ShowFrame; +import com.flagstone.transform.action.Action; +import com.flagstone.transform.action.BasicAction; +import com.flagstone.transform.coder.Coder; +import com.flagstone.transform.datatype.Bounds; +import com.flagstone.transform.datatype.CoordTransform; +import com.flagstone.transform.datatype.WebPalette; +import com.flagstone.transform.image.ImageTag; +import com.flagstone.transform.util.image.ImageDecoder; +import com.flagstone.transform.util.image.ImageRegistry; +import com.flagstone.transform.util.image.ImageShape; +import com.flagstone.transform.util.shape.Canvas; +import com.flagstone.transform.util.sound.SoundFactory; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.Image; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.ImageReference; +import com.hammurapi.jcapture.VideoEncoder.Fragment.Frame.Shape.ShapeContent; + +public class SwfEncoder implements VideoEncoder { + + @Override + public String getFileExtension() { + return "swf"; + } + + @Override + public String getMimeType() { + return "application/x-shockwave-flash"; + } + + @Override + public String toString() { + return "SWF"; + } + + @Override + public Dimension encode(Config config, + com.hammurapi.jcapture.Movie source, + OutputStream out) + throws Exception { + + AtomicInteger uid = new AtomicInteger(); + + /** + * For reusing shape id's. + */ + int maxId = Coder.USHORT_MAX; + + ButtonManager manager = null; + if (config.isToolBar()) { + manager = new ButtonManager(); + manager.loadLibrary(getClass().getResource("toolbar_buttons.swf")); + uid.set(manager.maxIdentifier()+1); + } + + Canvas path = new Canvas(); + path.setPixels(false); + + int minImgLayer = 10; + int imgLayer = minImgLayer; + int maxImgLayer = maxId - 1000; + + int mouseLayer = maxImgLayer+1; + int mouseUid = -1; + Place2 mousePlace = null; + ImageTag mouseImage = null; + + int layer = maxImgLayer+2; + + int totalWork = 0; + for (Fragment frg: source.getFragments()) { + totalWork = frg.getFrames().size()+1; + } + + ProgressMonitor progressMonitor = new ProgressMonitor(config.getParentComponent(), "Encoding to SWF", "Composing movie", 0, totalWork); + int progressCounter = 0; + + progressMonitor.setNote("Composing movie"); + boolean firstFrame = true; + Dimension ret = null; + Map imageCache = new IdentityHashMap(); + + Movie movie = new Movie(); + + Point prevMouseLocation = null; + + int frameNo = 0; + + for (Fragment fragment: source.getFragments()) { + + SoundFactory soundFactory = null; + boolean soundHeaderAdded = false; + File audio = fragment.getAudio(); + if (audio!=null) { + progressMonitor.setNote("Loading sound"); + soundFactory = new SoundFactory(); + + // MP3 conversion + if (config.getMp3command()!=null && config.getMp3command().trim().length()>0) { + audio = new File(audio.getAbsolutePath()+".mp3"); + Runtime runtime = Runtime.getRuntime(); + Process proc = runtime.exec(MessageFormat.format(config.getMp3command(), new Object[] {fragment.getAudio().getAbsolutePath(), audio.getAbsolutePath()})); + proc.waitFor(); + if (!fragment.getAudio().delete()) { + fragment.getAudio().deleteOnExit(); + } + } + + soundFactory.read(audio); + } + + progressMonitor.setProgress(++progressCounter); + + if (progressMonitor.isCanceled()) { + return null; + } + + for (Frame frame: fragment.getFrames()) { + if (progressMonitor.isCanceled()) { + return null; + } + + boolean addStop = false; + + ++frameNo; + + if (firstFrame) { + firstFrame = false; + + MovieHeader header = new MovieHeader(); + header.setCompressed(true); + header.setFrameRate(source.getFramesPerSecond()); + + int toolbarHeight = 29 * 20; + int toolbarWidth = 495 * 20; + + int toolbarX = 0; // - image.getWidth()*20/2; + int toolbarY = frame.getSize().height*20; + + int movieWidth = frame.getSize().width*20; + int movieHeight = frame.getSize().height*20; + if (config.isToolBar()) { + movieHeight+=toolbarHeight; + } + + ret = new Dimension(movieWidth/20, movieHeight/20); + + float toolbarScaleX = (float) movieWidth / (float) toolbarWidth; + float toolbarScaleY = 1.0f; + + Bounds movieBounds = new Bounds(0, 0, movieWidth, movieHeight); + header.setFrameSize(movieBounds); + movie.add(header); + movie.add(new Background(WebPalette.WHITE.color())); + + if (config.isToolBar()) { + // Add all the shapes etc used for buttons + List toolbarDefinitions = manager.getDefinitions(); + movie.getObjects().addAll(toolbarDefinitions); + + Place2 placeBackground = manager.getButton("background", layer++, 0, 0); + placeBackground.setTransform(new CoordTransform(toolbarScaleX, toolbarScaleY, 0, 0, toolbarX, toolbarY)); + + // Get the button to use and give its position + movie.add(placeBackground); + movie.add(manager.getButton("play_button", layer++, toolbarX + 500, toolbarY + toolbarHeight / 2)); + movie.add(manager.getButton("progress_bar", layer++, toolbarX + 1000, toolbarY + toolbarHeight / 2)); + movie.add(manager.getButton("volume_control", layer++, toolbarX + 5600, toolbarY + toolbarHeight / 2)); + + if (!config.isPlay()) { + addStop = true; + } + } + } + + if (!soundHeaderAdded && soundFactory!=null) { + movie.add(soundFactory.streamHeader(source.getFramesPerSecond())); + soundHeaderAdded = true; + } + + if (soundFactory!=null) { + MovieTag soundBlock = soundFactory.streamSound(); + if (soundBlock != null) { + movie.add(soundBlock); + } + } + + for (Shape shape: frame.getShapes()) { + if (shape.getContent().coversEverything() || imgLayer==maxImgLayer) { + for (int i=minImgLayer; i<=imgLayer; ++i) { + movie.add(new Remove2(i)); + } + imgLayer = minImgLayer; + } + + ShapeContent shapeContent = shape.getContent(); + Image image; + if (shapeContent instanceof Image) { + image = (Image) shapeContent; + } else if (shape.getContent() instanceof ImageReference) { + image = ((ImageReference) shapeContent).getImage(); + } else { + throw new IllegalArgumentException("Unexpected content type: "+shapeContent); + } + + ImageTag imageTag = imageCache.get(image); + if (imageTag==null) { + try { + ImageDecoder decoder = ImageRegistry.getImageProvider("image/"+config.getImageFormat().toLowerCase()); + decoder.read(new ByteArrayInputStream(image.getImage().getImageBytes())); + imageTag = decoder.defineImage(uid.incrementAndGet()); + imageCache.put(image, imageTag); + movie.add(imageTag); + } catch (Exception e) { + // Doing our best to create movie, even with flaws. + System.err.println("Error encoding image at frame "+frameNo+": "+e); + e.printStackTrace(); + if (JOptionPane.showConfirmDialog(config.getParentComponent(), + "Error encoding image ("+image.getImage().getWidth()+"*"+image.getImage().getHeight()+") at frame "+frameNo+": "+e+". Continue encoding?", + "Encoding error", + JOptionPane.YES_NO_OPTION, + JOptionPane.ERROR_MESSAGE)==JOptionPane.NO_OPTION) { + throw e; + } + } + } + + int shapeId = uid.incrementAndGet(); + DefineTag shapeTag = new ImageShape().defineShape(shapeId, imageTag); + Place2 place = Place2.show(shapeTag.getIdentifier(), imgLayer++, shape.getLocation().x*20, shape.getLocation().y*20); + movie.add(shapeTag); + movie.add(place); + } + + Point mouseLocation = frame.getMousePointer(); + if (mouseLocation!=null) { + if (mouseImage==null) { + BufferedImage mouseBi = ImageIO.read(getClass().getResource("mouse.png")); + if (config.getScreenScale()<0.99 || config.getScreenScale() > 1.01) { + BufferedImage scaled = new BufferedImage((int) (mouseBi.getWidth()*config.getScreenScale()), (int) (mouseBi.getHeight()*config.getScreenScale()), mouseBi.getType()); + Graphics2D g = scaled.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g.drawImage(mouseBi, 0, 0, scaled.getWidth(), scaled.getHeight(), null); + g.dispose(); + mouseBi = scaled; + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(mouseBi, "PNG", baos); + baos.close(); + ImageDecoder decoder = ImageRegistry.getImageProvider("image/png"); + decoder.read(new ByteArrayInputStream(baos.toByteArray())); + mouseImage = decoder.defineImage(uid.incrementAndGet()); + movie.add(mouseImage); + } + + if (!mouseLocation.equals(prevMouseLocation)) { + prevMouseLocation = mouseLocation; + mouseUid = uid.incrementAndGet(); + DefineTag mShape = new ImageShape().defineShape(uid.incrementAndGet(), mouseImage); //createRect(mouseUid, 100, 100, WebPalette.RED.color()); + if (mousePlace==null) { + mousePlace = Place2.show(mShape.getIdentifier(), mouseLayer, mouseLocation.x*20, mouseLocation.y*20); + } else { + mousePlace = Place2.replace(mShape.getIdentifier(), mouseLayer, mouseLocation.x*20, mouseLocation.y*20); + } + movie.add(mShape); + movie.add(mousePlace); + } + } else if (mouseUid!=-1) { + Remove remove = new Remove(mouseUid, mouseLayer); + movie.add(remove); + } + + if (addStop) { + DoAction cmd = new DoAction(new ArrayList()); + cmd.add(BasicAction.STOP); + movie.add(cmd); + } + movie.add(ShowFrame.getInstance()); + + progressMonitor.setProgress(++progressCounter); + } + + progressMonitor.setProgress(++progressCounter); + if (soundFactory!=null) { + progressMonitor.setNote("Recording trailing sound"); + MovieTag block; + while ((block = soundFactory.streamSound()) != null) { + movie.add(block); + movie.add(ShowFrame.getInstance()); + } + } + + if (audio!=null) { + if (!audio.delete()) { + audio.deleteOnExit(); + } + } + } + + if (!config.isLoop()) { + List actions = new ArrayList(); + actions.add(BasicAction.STOP); + actions.add(BasicAction.END); + DoAction doAction = new DoAction(actions); + movie.add(doAction); + movie.add(ShowFrame.getInstance()); + } + + progressMonitor.setProgress(++progressCounter); + progressMonitor.setNote("Encoding movie"); + movie.encodeToStream(out); + source.close(); + return ret; + } + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/Translucener.java b/plugin/jcapture/src/com/hammurapi/jcapture/Translucener.java new file mode 100644 index 000000000..33a537b7f --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/Translucener.java @@ -0,0 +1,17 @@ +package com.hammurapi.jcapture; + +import java.awt.Frame; + +abstract class Translucener { + + protected abstract void makeTranslucent(Frame frame); + + static void makeFrameTranslucent(Frame frame) throws Exception { + String jVersion = System.getProperty("java.version"); + if (jVersion==null || "1.6".equals(jVersion) || jVersion.startsWith("1.6.")) { + ((Translucener) Class.forName("com.hammurapi.jcapture.AWTUtilitiesTranslucener").newInstance()).makeTranslucent(frame); + } else { + ((Translucener) Class.forName("com.hammurapi.jcapture.GraphicsDeviceTranslucener").newInstance()).makeTranslucent(frame); + } + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/VideoEncoder.java b/plugin/jcapture/src/com/hammurapi/jcapture/VideoEncoder.java new file mode 100644 index 000000000..d32f2a834 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/VideoEncoder.java @@ -0,0 +1,164 @@ +package com.hammurapi.jcapture; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.io.File; +import java.io.OutputStream; +import java.util.List; + +/** + * This is a service interface to be implemented by video encoders. + * jCapture discovers encoders using java.util.ServiceLoader. + * + * All interfaces used by this interface are defined as nested for easy reference. + * @author Pavel + * + */ +public interface VideoEncoder { + + interface Config { + + /** + * + * @return true if encoder shall add a toolbar to the movie. + */ + boolean isToolBar(); + + /** + * + * @return true if movie shall be played in a loop. + */ + boolean isLoop(); + + /** + * + * @return true if movie shall start playing after downloading. + */ + boolean isPlay(); + + /** + * @return For scaling mouse pointer. + */ + double getScreenScale(); + + /** + * For progress monitor. + * @return + */ + Component getParentComponent(); + + String getImageFormat(); + + /** + * @return OS command to convert WAV to MP3 if encoder requires/benefits from it. + */ + String getMp3command(); + } + + /** + * Movie fragment is a collection of frames with associated audio. + * @author Pavel + * + */ + interface Fragment { + + /** + * Frame contains zero or more shapes and mouse location. + * @author Pavel + * + */ + interface Frame { + + boolean isActive(); + + /** + * Image shape to be placed on the screen. + * @author Pavel + * + */ + interface Shape { + + /** + * Base interface for shape content. + * @author Pavel + * + */ + interface ShapeContent { + + /** + * @return true if this shape covers the entire screen area. + */ + boolean coversEverything(); + } + + interface Image extends ShapeContent { + + MappedImage getImage(); + + } + + /** + * References already defined image. + * @author Pavel + * + */ + interface ImageReference extends ShapeContent { + + Image getImage(); + + } + + Point getLocation(); + + ShapeContent getContent(); + + } + + /** + * Frame's shapes. + * @return + */ + List getShapes(); + + Point getMousePointer(); + + Dimension getSize(); + + } + + /** + * Fragment frames. + * @return + */ + List getFrames(); + + /** + * Audio file (WAV). + * @return + */ + File getAudio(); + + } + + String getFileExtension(); + + String getMimeType(); + + /** + * This method shall return output format name, e.g. SWF. + * @return + */ + String toString(); + + /** + * Encodes video to output stream. + * @param fragments Fragments to encode + * @param out Output stream + * @param progressMonitor Progress monitor has work allocated for each frame plus one unit of work per fragment for sound decoding plus one unit for final encoding. + * @param progressCounter current progress counter position. + * @return movie size or null if operation was cancelled + */ + Dimension encode(Config config, Movie movie, OutputStream out) throws Exception; + +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/WavFile.java b/plugin/jcapture/src/com/hammurapi/jcapture/WavFile.java new file mode 100644 index 000000000..0c2d68e72 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/WavFile.java @@ -0,0 +1,787 @@ +package com.hammurapi.jcapture; + +// This file was taken from http://www.labbookpages.co.uk/audio/javaWavFiles.html + +// Wav file IO class +// A.Greensted +// http://www.labbookpages.co.uk + +// File format is based on the information from +// http://www.sonicspot.com/guide/wavefiles.html +// http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/wave.htm + +// Version 1.0 + +import java.io.*; + +public class WavFile { + private enum IOState { + READING, WRITING, CLOSED + }; + + private final static int BUFFER_SIZE = 4096; + + private final static int FMT_CHUNK_ID = 0x20746D66; + private final static int DATA_CHUNK_ID = 0x61746164; + private final static int RIFF_CHUNK_ID = 0x46464952; + private final static int RIFF_TYPE_ID = 0x45564157; + + private File file; // File that will be read from or written to + private IOState ioState; // Specifies the IO State of the Wav File (used for + // snaity checking) + private int bytesPerSample; // Number of bytes required to store a single + // sample + private long numFrames; // Number of frames within the data section + private FileOutputStream oStream; // Output stream used for writting data + private FileInputStream iStream; // Input stream used for reading data + private double floatScale; // Scaling factor used for int <-> float + // conversion + private double floatOffset; // Offset factor used for int <-> float + // conversion + private boolean wordAlignAdjust; // Specify if an extra byte at the end of + // the data chunk is required for word + // alignment + + // Wav Header + private int numChannels; // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535) + private long sampleRate; // 4 bytes unsigned, 0x00000001 (1) to 0xFFFFFFFF + // (4,294,967,295) + // Although a java int is 4 bytes, it is signed, + // so need to use a long + private int blockAlign; // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535) + private int validBits; // 2 bytes unsigned, 0x0002 (2) to 0xFFFF (65,535) + + // Buffering + private byte[] buffer; // Local buffer used for IO + private int bufferPointer; // Points to the current position in local buffer + private int bytesRead; // Bytes read after last read into local buffer + private long frameCounter; // Current number of frames read or written + + // Cannot instantiate WavFile directly, must either use newWavFile() or + // openWavFile() + private WavFile() { + buffer = new byte[BUFFER_SIZE]; + } + + public int getNumChannels() { + return numChannels; + } + + public long getNumFrames() { + return numFrames; + } + + public long getFramesRemaining() { + return numFrames - frameCounter; + } + + public long getSampleRate() { + return sampleRate; + } + + public int getValidBits() { + return validBits; + } + + public static WavFile newWavFile(File file, int numChannels, + long numFrames, int validBits, long sampleRate) throws IOException, + WavFileException { + // Instantiate new Wavfile and initialise + WavFile wavFile = new WavFile(); + wavFile.file = file; + wavFile.numChannels = numChannels; + wavFile.numFrames = numFrames; + wavFile.sampleRate = sampleRate; + wavFile.bytesPerSample = (validBits + 7) / 8; + wavFile.blockAlign = wavFile.bytesPerSample * numChannels; + wavFile.validBits = validBits; + + // Sanity check arguments + if (numChannels < 1 || numChannels > 65535) + throw new WavFileException( + "Illegal number of channels, valid range 1 to 65536"); + if (numFrames < 0) + throw new WavFileException("Number of frames must be positive"); + if (validBits < 2 || validBits > 65535) + throw new WavFileException( + "Illegal number of valid bits, valid range 2 to 65536"); + if (sampleRate < 0) + throw new WavFileException("Sample rate must be positive"); + + // Create output stream for writing data + wavFile.oStream = new FileOutputStream(file); + + // Calculate the chunk sizes + long dataChunkSize = wavFile.blockAlign * numFrames; + long mainChunkSize = 4 + // Riff Type + 8 + // Format ID and size + 16 + // Format data + 8 + // Data ID and size + dataChunkSize; + + // Chunks must be word aligned, so if odd number of audio data bytes + // adjust the main chunk size + if (dataChunkSize % 2 == 1) { + mainChunkSize += 1; + wavFile.wordAlignAdjust = true; + } else { + wavFile.wordAlignAdjust = false; + } + + // Set the main chunk size + putLE(RIFF_CHUNK_ID, wavFile.buffer, 0, 4); + putLE(mainChunkSize, wavFile.buffer, 4, 4); + putLE(RIFF_TYPE_ID, wavFile.buffer, 8, 4); + + // Write out the header + wavFile.oStream.write(wavFile.buffer, 0, 12); + + // Put format data in buffer + long averageBytesPerSecond = sampleRate * wavFile.blockAlign; + + putLE(FMT_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID + putLE(16, wavFile.buffer, 4, 4); // Chunk Data Size + putLE(1, wavFile.buffer, 8, 2); // Compression Code (Uncompressed) + putLE(numChannels, wavFile.buffer, 10, 2); // Number of channels + putLE(sampleRate, wavFile.buffer, 12, 4); // Sample Rate + putLE(averageBytesPerSecond, wavFile.buffer, 16, 4); // Average Bytes + // Per Second + putLE(wavFile.blockAlign, wavFile.buffer, 20, 2); // Block Align + putLE(validBits, wavFile.buffer, 22, 2); // Valid Bits + + // Write Format Chunk + wavFile.oStream.write(wavFile.buffer, 0, 24); + + // Start Data Chunk + putLE(DATA_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID + putLE(dataChunkSize, wavFile.buffer, 4, 4); // Chunk Data Size + + // Write Format Chunk + wavFile.oStream.write(wavFile.buffer, 0, 8); + + // Calculate the scaling factor for converting to a normalised double + if (wavFile.validBits > 8) { + // If more than 8 validBits, data is signed + // Conversion required multiplying by magnitude of max positive + // value + wavFile.floatOffset = 0; + wavFile.floatScale = Long.MAX_VALUE >> (64 - wavFile.validBits); + } else { + // Else if 8 or less validBits, data is unsigned + // Conversion required dividing by max positive value + wavFile.floatOffset = 1; + wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1); + } + + // Finally, set the IO State + wavFile.bufferPointer = 0; + wavFile.bytesRead = 0; + wavFile.frameCounter = 0; + wavFile.ioState = IOState.WRITING; + + return wavFile; + } + + public static WavFile openWavFile(File file) throws IOException, + WavFileException { + // Instantiate new Wavfile and store the file reference + WavFile wavFile = new WavFile(); + wavFile.file = file; + + // Create a new file input stream for reading file data + wavFile.iStream = new FileInputStream(file); + + // Read the first 12 bytes of the file + int bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 12); + if (bytesRead != 12) + throw new WavFileException("Not enough wav file bytes for header"); + + // Extract parts from the header + long riffChunkID = getLE(wavFile.buffer, 0, 4); + long chunkSize = getLE(wavFile.buffer, 4, 4); + long riffTypeID = getLE(wavFile.buffer, 8, 4); + + // Check the header bytes contains the correct signature + if (riffChunkID != RIFF_CHUNK_ID) + throw new WavFileException( + "Invalid Wav Header data, incorrect riff chunk ID"); + if (riffTypeID != RIFF_TYPE_ID) + throw new WavFileException( + "Invalid Wav Header data, incorrect riff type ID"); + + // Check that the file size matches the number of bytes listed in header + if (file.length() != chunkSize + 8) { + throw new WavFileException("Header chunk size (" + chunkSize + + ") does not match file size (" + file.length() + ")"); + } + + boolean foundFormat = false; + boolean foundData = false; + + // Search for the Format and Data Chunks + while (true) { + // Read the first 8 bytes of the chunk (ID and chunk size) + bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 8); + if (bytesRead == -1) + throw new WavFileException( + "Reached end of file without finding format chunk"); + if (bytesRead != 8) + throw new WavFileException("Could not read chunk header"); + + // Extract the chunk ID and Size + long chunkID = getLE(wavFile.buffer, 0, 4); + chunkSize = getLE(wavFile.buffer, 4, 4); + + // Word align the chunk size + // chunkSize specifies the number of bytes holding data. However, + // the data should be word aligned (2 bytes) so we need to calculate + // the actual number of bytes in the chunk + long numChunkBytes = (chunkSize % 2 == 1) ? chunkSize + 1 + : chunkSize; + + if (chunkID == FMT_CHUNK_ID) { + // Flag that the format chunk has been found + foundFormat = true; + + // Read in the header info + bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 16); + + // Check this is uncompressed data + int compressionCode = (int) getLE(wavFile.buffer, 0, 2); + if (compressionCode != 1) + throw new WavFileException("Compression Code " + + compressionCode + " not supported"); + + // Extract the format information + wavFile.numChannels = (int) getLE(wavFile.buffer, 2, 2); + wavFile.sampleRate = getLE(wavFile.buffer, 4, 4); + wavFile.blockAlign = (int) getLE(wavFile.buffer, 12, 2); + wavFile.validBits = (int) getLE(wavFile.buffer, 14, 2); + + if (wavFile.numChannels == 0) + throw new WavFileException( + "Number of channels specified in header is equal to zero"); + if (wavFile.blockAlign == 0) + throw new WavFileException( + "Block Align specified in header is equal to zero"); + if (wavFile.validBits < 2) + throw new WavFileException( + "Valid Bits specified in header is less than 2"); + if (wavFile.validBits > 64) + throw new WavFileException( + "Valid Bits specified in header is greater than 64, this is greater than a long can hold"); + + // Calculate the number of bytes required to hold 1 sample + wavFile.bytesPerSample = (wavFile.validBits + 7) / 8; + if (wavFile.bytesPerSample * wavFile.numChannels != wavFile.blockAlign) + throw new WavFileException( + "Block Align does not agree with bytes required for validBits and number of channels"); + + // Account for number of format bytes and then skip over + // any extra format bytes + numChunkBytes -= 16; + if (numChunkBytes > 0) + wavFile.iStream.skip(numChunkBytes); + } else if (chunkID == DATA_CHUNK_ID) { + // Check if we've found the format chunk, + // If not, throw an exception as we need the format information + // before we can read the data chunk + if (foundFormat == false) + throw new WavFileException( + "Data chunk found before Format chunk"); + + // Check that the chunkSize (wav data length) is a multiple of + // the + // block align (bytes per frame) + if (chunkSize % wavFile.blockAlign != 0) + throw new WavFileException( + "Data Chunk size is not multiple of Block Align"); + + // Calculate the number of frames + wavFile.numFrames = chunkSize / wavFile.blockAlign; + + // Flag that we've found the wave data chunk + foundData = true; + + break; + } else { + // If an unknown chunk ID is found, just skip over the chunk + // data + wavFile.iStream.skip(numChunkBytes); + } + } + + // Throw an exception if no data chunk has been found + if (foundData == false) + throw new WavFileException("Did not find a data chunk"); + + // Calculate the scaling factor for converting to a normalised double + if (wavFile.validBits > 8) { + // If more than 8 validBits, data is signed + // Conversion required dividing by magnitude of max negative value + wavFile.floatOffset = 0; + wavFile.floatScale = 1 << (wavFile.validBits - 1); + } else { + // Else if 8 or less validBits, data is unsigned + // Conversion required dividing by max positive value + wavFile.floatOffset = -1; + wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1); + } + + wavFile.bufferPointer = 0; + wavFile.bytesRead = 0; + wavFile.frameCounter = 0; + wavFile.ioState = IOState.READING; + + return wavFile; + } + + // Get and Put little endian data from local buffer + // ------------------------------------------------ + private static long getLE(byte[] buffer, int pos, int numBytes) { + numBytes--; + pos += numBytes; + + long val = buffer[pos] & 0xFF; + for (int b = 0; b < numBytes; b++) + val = (val << 8) + (buffer[--pos] & 0xFF); + + return val; + } + + private static void putLE(long val, byte[] buffer, int pos, int numBytes) { + for (int b = 0; b < numBytes; b++) { + buffer[pos] = (byte) (val & 0xFF); + val >>= 8; + pos++; + } + } + + // Sample Writing and Reading + // -------------------------- + private void writeSample(long val) throws IOException { + for (int b = 0; b < bytesPerSample; b++) { + if (bufferPointer == BUFFER_SIZE) { + oStream.write(buffer, 0, BUFFER_SIZE); + bufferPointer = 0; + } + + buffer[bufferPointer] = (byte) (val & 0xFF); + val >>= 8; + bufferPointer++; + } + } + + private long readSample() throws IOException, WavFileException { + long val = 0; + + for (int b = 0; b < bytesPerSample; b++) { + if (bufferPointer == bytesRead) { + int read = iStream.read(buffer, 0, BUFFER_SIZE); + if (read == -1) + throw new WavFileException("Not enough data available"); + bytesRead = read; + bufferPointer = 0; + } + + int v = buffer[bufferPointer]; + if (b < bytesPerSample - 1 || bytesPerSample == 1) + v &= 0xFF; + val += v << (b * 8); + + bufferPointer++; + } + + return val; + } + + // Integer + // ------- + public int readFrames(int[] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(int[] sampleBuffer, int offset, int numFramesToRead) + throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + sampleBuffer[offset] = (int) readSample(); + offset++; + } + + frameCounter++; + } + + return numFramesToRead; + } + + public int readFrames(int[][] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(int[][] sampleBuffer, int offset, int numFramesToRead) + throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + sampleBuffer[c][offset] = (int) readSample(); + + offset++; + frameCounter++; + } + + return numFramesToRead; + } + + public int writeFrames(int[] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(int[] sampleBuffer, int offset, int numFramesToWrite) + throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + writeSample(sampleBuffer[offset]); + offset++; + } + + frameCounter++; + } + + return numFramesToWrite; + } + + public int writeFrames(int[][] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(int[][] sampleBuffer, int offset, + int numFramesToWrite) throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + writeSample(sampleBuffer[c][offset]); + + offset++; + frameCounter++; + } + + return numFramesToWrite; + } + + // Long + // ---- + public int readFrames(long[] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(long[] sampleBuffer, int offset, int numFramesToRead) + throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + sampleBuffer[offset] = readSample(); + offset++; + } + + frameCounter++; + } + + return numFramesToRead; + } + + public int readFrames(long[][] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(long[][] sampleBuffer, int offset, int numFramesToRead) + throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + sampleBuffer[c][offset] = readSample(); + + offset++; + frameCounter++; + } + + return numFramesToRead; + } + + public int writeFrames(long[] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(long[] sampleBuffer, int offset, int numFramesToWrite) + throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + writeSample(sampleBuffer[offset]); + offset++; + } + + frameCounter++; + } + + return numFramesToWrite; + } + + public int writeFrames(long[][] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(long[][] sampleBuffer, int offset, + int numFramesToWrite) throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + writeSample(sampleBuffer[c][offset]); + + offset++; + frameCounter++; + } + + return numFramesToWrite; + } + + // Double + // ------ + public int readFrames(double[] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(double[] sampleBuffer, int offset, int numFramesToRead) + throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + sampleBuffer[offset] = floatOffset + (double) readSample() + / floatScale; + offset++; + } + + frameCounter++; + } + + return numFramesToRead; + } + + public int readFrames(double[][] sampleBuffer, int numFramesToRead) + throws IOException, WavFileException { + return readFrames(sampleBuffer, 0, numFramesToRead); + } + + public int readFrames(double[][] sampleBuffer, int offset, + int numFramesToRead) throws IOException, WavFileException { + if (ioState != IOState.READING) + throw new IOException("Cannot read from WavFile instance"); + + for (int f = 0; f < numFramesToRead; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + sampleBuffer[c][offset] = floatOffset + (double) readSample() + / floatScale; + + offset++; + frameCounter++; + } + + return numFramesToRead; + } + + public int writeFrames(double[] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(double[] sampleBuffer, int offset, + int numFramesToWrite) throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) { + writeSample((long) (floatScale * (floatOffset + sampleBuffer[offset]))); + offset++; + } + + frameCounter++; + } + + return numFramesToWrite; + } + + public int writeFrames(double[][] sampleBuffer, int numFramesToWrite) + throws IOException, WavFileException { + return writeFrames(sampleBuffer, 0, numFramesToWrite); + } + + public int writeFrames(double[][] sampleBuffer, int offset, + int numFramesToWrite) throws IOException, WavFileException { + if (ioState != IOState.WRITING) + throw new IOException("Cannot write to WavFile instance"); + + for (int f = 0; f < numFramesToWrite; f++) { + if (frameCounter == numFrames) + return f; + + for (int c = 0; c < numChannels; c++) + writeSample((long) (floatScale * (floatOffset + sampleBuffer[c][offset]))); + + offset++; + frameCounter++; + } + + return numFramesToWrite; + } + + public void close() throws IOException { + // Close the input stream and set to null + if (iStream != null) { + iStream.close(); + iStream = null; + } + + if (oStream != null) { + // Write out anything still in the local buffer + if (bufferPointer > 0) + oStream.write(buffer, 0, bufferPointer); + + // If an extra byte is required for word alignment, add it to the + // end + if (wordAlignAdjust) + oStream.write(0); + + // Close the stream and set to null + oStream.close(); + oStream = null; + } + + // Flag that the stream is closed + ioState = IOState.CLOSED; + } + + public void display() { + display(System.out); + } + + public void display(PrintStream out) { + out.printf("File: %s\n", file); + out.printf("Channels: %d, Frames: %d\n", numChannels, numFrames); + out.printf("IO State: %s\n", ioState); + out.printf("Sample Rate: %d, Block Align: %d\n", sampleRate, blockAlign); + out.printf("Valid Bits: %d, Bytes per sample: %d\n", validBits, + bytesPerSample); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Must supply filename"); + System.exit(1); + } + + try { + for (String filename : args) { + WavFile readWavFile = openWavFile(new File(filename)); + readWavFile.display(); + + long numFrames = readWavFile.getNumFrames(); + int numChannels = readWavFile.getNumChannels(); + int validBits = readWavFile.getValidBits(); + long sampleRate = readWavFile.getSampleRate(); + + WavFile writeWavFile = newWavFile(new File("out.wav"), + numChannels, numFrames, validBits, sampleRate); + + final int BUF_SIZE = 5001; + + // int[] buffer = new int[BUF_SIZE * numChannels]; + // long[] buffer = new long[BUF_SIZE * numChannels]; + double[] buffer = new double[BUF_SIZE * numChannels]; + + int framesRead = 0; + int framesWritten = 0; + + do { + framesRead = readWavFile.readFrames(buffer, BUF_SIZE); + framesWritten = writeWavFile.writeFrames(buffer, BUF_SIZE); + System.out.printf("%d %d\n", framesRead, framesWritten); + } while (framesRead != 0); + + readWavFile.close(); + writeWavFile.close(); + } + + WavFile writeWavFile = newWavFile(new File("out2.wav"), 1, 10, 23, + 44100); + double[] buffer = new double[10]; + writeWavFile.writeFrames(buffer, 10); + writeWavFile.close(); + } catch (Exception e) { + System.err.println(e); + e.printStackTrace(); + } + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/WavFileException.java b/plugin/jcapture/src/com/hammurapi/jcapture/WavFileException.java new file mode 100644 index 000000000..64e27feb7 --- /dev/null +++ b/plugin/jcapture/src/com/hammurapi/jcapture/WavFileException.java @@ -0,0 +1,26 @@ +package com.hammurapi.jcapture; + +//This file was taken from http://www.labbookpages.co.uk/audio/javaWavFiles.html and package declaration was added. + +public class WavFileException extends Exception +{ + public WavFileException() + { + super(); + } + + public WavFileException(String message) + { + super(message); + } + + public WavFileException(String message, Throwable cause) + { + super(message, cause); + } + + public WavFileException(Throwable cause) + { + super(cause); + } +} diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/camera.png b/plugin/jcapture/src/com/hammurapi/jcapture/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..f7cbba0a8ca3b25828052df27899b798919eedee GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`*`6+rAr-fhQx*sq1YK_U|3u<4%)3LY?cTFJ`De2``cnZUk8YQY`01%Ff)@it^VV|>K7B7jw(-qEaO0^|FJ zDJT8|fn$iF5#vg82UDjK;|3%B0GW;yS7py#f(;W_RVF6zFfh0;^%i^|buIvCErX}4 KpUXO@geCxWnm3>T literal 0 HcmV?d00001 diff --git a/plugin/jcapture/src/com/hammurapi/jcapture/mouse.png b/plugin/jcapture/src/com/hammurapi/jcapture/mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..59c10fd2428d6e09c81040c0049f3ba890c7ea9a GIT binary patch literal 519 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 CxC}o4 literal 0 HcmV?d00001 diff --git a/plugin/jcapture/upload.php b/plugin/jcapture/upload.php new file mode 100644 index 000000000..ae4ce2a96 --- /dev/null +++ b/plugin/jcapture/upload.php @@ -0,0 +1,62 @@ +getInstalledPlugins(); +$capturePluginInstalled = in_array('jcapture', $pluginList); +if (!$capturePluginInstalled) { + exit; +} + +if (!isset($_FILES['Filedata'])) { + exit; +} + +$courseInfo = api_get_course_info(); +$folderName = 'captures'; +$documentId = DocumentManager::get_document_id($courseInfo, '/'.$folderName); +$path = null; +if (empty($documentId)) { + $course_dir = $courseInfo['path'].'/document'; + $sys_course_path = api_get_path(SYS_COURSE_PATH); + $dir = $sys_course_path.$course_dir; + $createdDir = create_unexisting_directory( + $courseInfo, + api_get_user_id(), + api_get_session_id(), + null, + null, + $dir, + '/'.$folderName, + $folderName + ); + if ($createdDir) { + $path = '/'.$folderName; + } +} else { + $data = DocumentManager::get_document_data_by_id($documentId, $courseInfo['code']); + $path = $data['path']; +} + +if (empty($path)) { + exit; +} + +$files = [ + 'file' => $_FILES['Filedata'], +]; + +DocumentManager::upload_document( + $files, + $path, + $_FILES['Filedata']['name'], + null, + false, + 'rename', + false, + true +); diff --git a/plugin/justification/Justification.php b/plugin/justification/Justification.php new file mode 100644 index 000000000..722e2c165 --- /dev/null +++ b/plugin/justification/Justification.php @@ -0,0 +1,101 @@ + 'boolean', + 'notification_to_creator_only' => 'boolean', + 'access_for_session_admin' => 'boolean', + 'default_course_id' => 'text', + ] + ); + } + + /** + * @return $this + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + public function getJustification($id) + { + $id = (int) $id; + + $sql = 'SELECT * FROM justification_document WHERE id = '.$id; + $query = Database::query($sql); + + return Database::fetch_array($query, 'ASSOC'); + } + + public function getUserJustificationList($userId) + { + $userId = (int) $userId; + + $sql = "SELECT * FROM justification_document_rel_users WHERE user_id = $userId "; + $query = Database::query($sql); + + return Database::store_result($query, 'ASSOC'); + } + + public function getUserJustification($id) + { + $id = (int) $id; + + $sql = "SELECT * FROM justification_document_rel_users WHERE id = $id "; + $query = Database::query($sql); + + return Database::fetch_array($query, 'ASSOC'); + } + + public function getList() + { + $sql = 'SELECT * FROM justification_document ORDER BY name '; + $query = Database::query($sql); + + return Database::store_result($query, 'ASSOC'); + } + + /** + * Install. + */ + public function install() + { + $sql = "CREATE TABLE IF NOT EXISTS justification_document ( + id INT unsigned NOT NULL auto_increment PRIMARY KEY, + code TEXT NULL, + name TEXT NULL, + validity_duration INT, + comment TEXT NULL, + date_manual_on INT + )"; + Database::query($sql); + + $sql = "CREATE TABLE IF NOT EXISTS justification_document_rel_users ( + id INT unsigned NOT NULL auto_increment PRIMARY KEY, + justification_document_id INT NOT NULL, + file_path VARCHAR(255), + user_id INT, + date_validity DATE + )"; + Database::query($sql); + } + + public function uninstall() + { + $sql = 'DROP TABLE IF EXISTS justification_document'; + Database::query($sql); + + $sql = 'DROP TABLE IF EXISTS justification_document_rel_users'; + Database::query($sql); + } +} diff --git a/plugin/justification/README.md b/plugin/justification/README.md new file mode 100644 index 000000000..bc4406d38 --- /dev/null +++ b/plugin/justification/README.md @@ -0,0 +1,39 @@ +Justification +============== + +1. Enable the plugin. +2. Create the justification files in plugin/justification/list.php + + +When activating the justification plugin it adds a link at the bottom of the plateform block of the administration page to open plugin/justification/list.php to create and manage the justification needed. +On this page the admin can manage the list of justification that would be asked to users, and select a course for automatic inscription if the justificatives are completed and desinscription if justicatives are not completed or outdated. For the automatic subscription and removal you need to create a cron to run plugin/justification/cron.php periodically to do the verification. +The justification plugin only activate a new tab in the profile for the user to upload some justification (the list of which are defined by the administrator on the page plugin/justification/list.php indicated above). +If the notification system is activated then you have the ability to create notification to be sent before the expiration of the justification if configured so. +To activate it you have to set it in app/config/configuration.php with : +``` +// Show notification events +/*CREATE TABLE IF NOT EXISTS notification_event ( +id INT unsigned NOT NULL auto_increment PRIMARY KEY, + title VARCHAR(255), + content TEXT, + link TEXT, + persistent INT, + day_diff INT, + event_type VARCHAR(255) + ); +ALTER TABLE notification_event ADD COLUMN event_id INT NULL; +CREATE TABLE IF NOT EXISTS notification_event_rel_user ( + id INT UNSIGNED AUTO_INCREMENT NOT NULL, + event_id INT unsigned, + user_id INT, + INDEX FK_EVENT (event_id), + INDEX FK_USER (user_id), + PRIMARY KEY (id) +); +ALTER TABLE notification_event_rel_user ADD CONSTRAINT FK_EVENT FOREIGN KEY (event_id) REFERENCES notification_event (id) ON DELETE CASCADE; +ALTER TABLE notification_event_rel_user ADD CONSTRAINT FK_USER FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE; +*/ +// create new user text extra field called 'notification_event' to save the persistent settings. +// $_configuration['notification_event'] = false; +``` + diff --git a/plugin/justification/add.php b/plugin/justification/add.php new file mode 100644 index 000000000..ed5629b01 --- /dev/null +++ b/plugin/justification/add.php @@ -0,0 +1,68 @@ +addText('name', get_lang('Name')); +$form->addText('code', $plugin->get_lang('JustificationCode')); +$form->addNumeric('validity_duration', $plugin->get_lang('ValidityDuration')); +$form->addCheckBox('date_manual_on', $plugin->get_lang('DateManualOn')); +$form->addTextarea('comment', get_lang('Comment')); +$form->addButtonSave(get_lang('Save')); + +if ($form->validate()) { + $values = $form->getSubmitValues(); + $dateManual = isset($values['date_manual_on']) ? 1 : 0; + + $cleanedCode = api_replace_dangerous_char($values['code']); + $code = Database::escape_string($cleanedCode); + + $sql = "SELECT * FROM justification_document WHERE code = '$code' "; + $result = Database::query($sql); + $data = Database::fetch_array($result); + $message = Display::return_message(get_lang('ThisCodeAlradyExists'), 'warning'); + + if (empty($data)) { + $params = [ + 'name' => $values['name'], + 'code' => $cleanedCode, + 'validity_duration' => $values['validity_duration'], + 'date_manual_on' => $dateManual, + 'comment' => $values['comment'], + ]; + Database::insert('justification_document', $params); + $message = Display::return_message(get_lang('Saved')); + } + + Display::addFlash($message); + + $url = api_get_path(WEB_PLUGIN_PATH).'justification/list.php?'; + header('Location: '.$url); + exit; +} + +$actionLinks = Display::toolbarButton( + $plugin->get_lang('Back'), + api_get_path(WEB_PLUGIN_PATH).'justification/list.php', + 'arrow-left', + 'primary' +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) +); + +$content = $form->returnForm(); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/justification/cron.php b/plugin/justification/cron.php new file mode 100644 index 000000000..d03992f3d --- /dev/null +++ b/plugin/justification/cron.php @@ -0,0 +1,88 @@ +getList(); +$totalFields = count($fieldList); + +if (empty($fieldList)) { + echo 'No fields to check. Please add them in the justification plugin'; + exit; +} + +$userList = UserManager::get_user_list(); +$count = count($userList); + +echo "#$count users found".PHP_EOL; +$currentDate = api_get_utc_datetime(); + +foreach ($userList as $user) { + $userId = $user['id']; + + echo "Checking user id #$userId".PHP_EOL; + + $userJustificationList = $plugin->getUserJustificationList($userId); + $userJustificationDocumentList = array_column($userJustificationList, 'date_validity', 'justification_document_id'); + + if (count($userJustificationList) < $totalFields) { + unsubscribeUser($userId, $courseInfo); + continue; + } + + if (count($userJustificationList) >= $totalFields) { + $successList = []; + foreach ($fieldList as $field) { + if (isset($userJustificationDocumentList[$field['id']])) { + $dateValidity = $userJustificationDocumentList[$field['id']]; + if ($dateValidity > $currentDate) { + $successList[] = true; + } + } + } + $countSuccess = count($successList); + if ($countSuccess === $totalFields) { + subscribeUser($userId, $courseInfo); + continue; + } else { + echo "User #$userId only got $countSuccess justification(s) out of $totalFields.".PHP_EOL; + } + } + + unsubscribeUser($userId, $courseInfo); +} + +function unsubscribeUser($userId, $courseInfo) +{ + $courseId = $courseInfo['real_id']; + CourseManager::unsubscribe_user($userId, $courseInfo['code']); + echo "Unsubscribe user id #$userId to course #$courseId".PHP_EOL; +} + +function subscribeUser($userId, $courseInfo) +{ + $courseId = $courseInfo['real_id']; + $isUserSubscribed = CourseManager::is_user_subscribed_in_course($userId, $courseInfo['code']); + if ($isUserSubscribed === false) { + CourseManager::subscribeUser($userId, $courseInfo['code'], STUDENT); + echo "Subscribe user id #$userId to course #$courseId".PHP_EOL; + } else { + echo "Nothing to do user id #$userId is already subscribed to #$courseId".PHP_EOL; + } +} diff --git a/plugin/justification/edit.php b/plugin/justification/edit.php new file mode 100644 index 000000000..2852732ea --- /dev/null +++ b/plugin/justification/edit.php @@ -0,0 +1,76 @@ +getJustification($id); + +$tpl = new Template($tool); +$fields = []; + +$form = new FormValidator('add', 'post', api_get_self().'?id='.$id); +$form->addText('name', get_lang('Name')); +$form->addText('code', $plugin->get_lang('JustificationCode')); +$form->addNumeric('validity_duration', $plugin->get_lang('ValidityDuration')); +$form->addCheckBox('date_manual_on', $plugin->get_lang('DateManualOn')); +$form->addTextarea('comment', get_lang('Comment')); +$form->addButtonSave(get_lang('Update')); + +$form->setDefaults($justification); + +if ($form->validate()) { + $values = $form->getSubmitValues(); + $cleanedCode = api_replace_dangerous_char($values['code']); + $code = Database::escape_string($cleanedCode); + + $sql = "SELECT * FROM justification_document WHERE code = '$code' AND id <> $id"; + $result = Database::query($sql); + $data = Database::fetch_array($result); + $message = Display::return_message(get_lang('ThisCodeAlradyExists'), 'warning'); + if (empty($data)) { + $params = [ + 'name' => $values['name'], + 'code' => $cleanedCode, + 'validity_duration' => $values['validity_duration'], + 'date_manual_on' => (int) $values['date_manual_on'], + 'comment' => $values['comment'], + ]; + + Database::update('justification_document', $params, ['id = ?' => $id]); + $message = Display::return_message(get_lang('Saved')); + } + + Display::addFlash($message); + + $url = api_get_path(WEB_PLUGIN_PATH).'justification/list.php?'; + header('Location: '.$url); + exit; +} + +$actionLinks = Display::toolbarButton( + $plugin->get_lang('Back'), + api_get_path(WEB_PLUGIN_PATH).'justification/list.php', + 'arrow-left', + 'primary' +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) +); + +$content = $form->returnForm(); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/justification/index.php b/plugin/justification/index.php new file mode 100644 index 000000000..e1533d2fe --- /dev/null +++ b/plugin/justification/index.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/justification/justification_by_user.php b/plugin/justification/justification_by_user.php new file mode 100644 index 000000000..e663db404 --- /dev/null +++ b/plugin/justification/justification_by_user.php @@ -0,0 +1,114 @@ +addHeader('Search'); +$form->addSelectAjax( + 'user_id', + get_lang('User'), + [], + [ + 'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like', + ] +); +$form->addButtonSearch(get_lang('Search')); +$tpl->assign('form', $form->returnForm()); + +$userId = isset($_REQUEST['user_id']) ? (int) $_REQUEST['user_id'] : 0; + +if ($form->validate()) { + $userId = $form->getSubmitValue('user_id'); +} + +if ($userId) { + $tpl->assign('user_info', api_get_user_info($userId)); + $list = $plugin->getUserJustificationList($userId); + if ($list) { + foreach ($list as &$item) { + if ($item['date_validity'] < api_get_local_time()) { + $item['date_validity'] = Display::label($item['date_validity'], 'warning'); + } + $item['justification'] = $plugin->getJustification($item['justification_document_id']); + $item['file_path'] = Display::url( + $item['file_path'], + api_get_uploaded_web_url('justification', $item['id'], $item['file_path']), + ['target' => '_blank'] + ); + } + } + if (empty($list)) { + Display::addFlash(Display::return_message($plugin->get_lang('NoJustificationFound'))); + } + $tpl->assign('list', $list); +} + +$tpl->assign('user_id', $userId); +$content = $tpl->fetch('justification/view/justification_user_list.tpl'); + +$actionLinks = ''; + +$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : ''; +$id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; + +switch ($action) { + case 'edit': + $userJustification = $plugin->getUserJustification($id); + $userInfo = api_get_user_info($userJustification['user_id']); + $form = new FormValidator('edit', 'post', api_get_self().'?a=edit&id='.$id.'&user_id='.$userId); + $form->addHeader($userInfo['complete_name']); + $element = $form->addDatePicker('date_validity', $plugin->get_lang('ValidityDate')); + $element->setValue($userJustification['date_validity']); + $form->addButtonUpdate(get_lang('Update')); + $form->setDefaults($userJustification); + $content = $form->returnForm(); + + if ($form->validate()) { + $values = $form->getSubmitValues(); + $date = Database::escape_string($values['date_validity']); + $sql = "UPDATE justification_document_rel_users SET date_validity = '$date' WHERE id = $id"; + Database::query($sql); + Display::addFlash(Display::return_message(get_lang('Updated'))); + header('Location: '.api_get_self().'?user_id='.$userId); + exit; + } + break; + case 'delete': + $userJustification = $plugin->getUserJustification($id); + if ($userJustification) { + api_remove_uploaded_file_by_id('justification', $id, $userJustification['file_path']); + + $sql = "DELETE FROM justification_document_rel_users WHERE id = $id"; + Database::query($sql); + + Display::addFlash(Display::return_message(get_lang('Deleted'))); + } + header('Location: '.api_get_self().'?user_id='.$userId); + exit; + break; +} + +$actionLinks .= Display::toolbarButton( + $plugin->get_lang('Back'), + api_get_path(WEB_PLUGIN_PATH).'justification/list.php', + 'arrow-left', + 'primary' +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) +); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/justification/lang/english.php b/plugin/justification/lang/english.php new file mode 100644 index 000000000..6d7a7a3ad --- /dev/null +++ b/plugin/justification/lang/english.php @@ -0,0 +1,27 @@ +getList(); + +$tpl->assign('list', $list); + +$content = $tpl->fetch('justification/view/list.tpl'); +$actionLinks = ''; +$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : ''; +$id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; + +switch ($action) { + case 'delete': + $sql = "DELETE FROM justification_document WHERE id = $id"; + Database::query($sql); + + Display::addFlash(Display::return_message(get_lang('Deleted'))); + header('Location: '.api_get_self()); + exit; + break; +} + +if (api_is_platform_admin()) { + $actionLinks .= Display::toolbarButton( + $plugin->get_lang('Add'), + api_get_path(WEB_PLUGIN_PATH).'justification/add.php', + 'plus', + 'primary' + ); +} + +$actionLinks .= Display::toolbarButton( + $plugin->get_lang('Users'), + api_get_path(WEB_PLUGIN_PATH).'justification/justification_by_user.php', + 'user', + 'primary' +); + +if (api_is_platform_admin()) { + $actionLinks .= Display::toolbarButton( + $plugin->get_lang('SetNewCourse'), + api_get_path(WEB_PLUGIN_PATH).'justification/set_course.php', + 'book', + 'primary' + ); +} + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) +); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/justification/plugin.php b/plugin/justification/plugin.php new file mode 100644 index 000000000..02126ea19 --- /dev/null +++ b/plugin/justification/plugin.php @@ -0,0 +1,6 @@ +get_info(); diff --git a/plugin/justification/set_course.php b/plugin/justification/set_course.php new file mode 100644 index 000000000..1fdecfb6d --- /dev/null +++ b/plugin/justification/set_course.php @@ -0,0 +1,57 @@ +addHeader($plugin->get_lang('SetNewCourse')); +$currentCourse = api_get_setting('justification_default_course_id', 'justification'); + +if (!empty($currentCourse)) { + $courseInfo = api_get_course_info_by_id($currentCourse); + Display::addFlash(Display::return_message(get_lang('Course').': '.$courseInfo['title'])); +} + +$form->addSelectAjax( + 'course_id', + get_lang('Course'), + null, + [ + 'url' => api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_course', + ] +); +$form->addButtonSave(get_lang('Save')); + +if ($form->validate()) { + $values = $form->getSubmitValues(); + api_set_setting('justification_default_course_id', $values['course_id']); + Display::addFlash(Display::return_message(get_lang('Saved'))); + $url = api_get_path(WEB_PLUGIN_PATH).'justification/list.php?'; + header('Location: '.$url); + exit; +} + +$actionLinks = Display::toolbarButton( + $plugin->get_lang('Back'), + api_get_path(WEB_PLUGIN_PATH).'justification/list.php', + 'arrow-left', + 'primary' +); + +$tpl->assign( + 'actions', + Display::toolbarAction('toolbar', [$actionLinks]) +); + +$content = $form->returnForm(); + +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/justification/uninstall.php b/plugin/justification/uninstall.php new file mode 100644 index 000000000..a1e3b57a1 --- /dev/null +++ b/plugin/justification/uninstall.php @@ -0,0 +1,8 @@ +uninstall(); diff --git a/plugin/justification/view/add.tpl b/plugin/justification/view/add.tpl new file mode 100644 index 000000000..078f032f1 --- /dev/null +++ b/plugin/justification/view/add.tpl @@ -0,0 +1,48 @@ +{{ search_form }} + + + + + + + + + + + + + + {% for meeting in meetings %} + + {% if meeting.visibility == 0 %} + + {% else %} + + {% endif %} + + + + + + + + {% endfor %} + +
    {{ 'CreatedAt'|get_lang }}{{ 'Status'|get_lang }}{{ 'Records'|get_plugin_lang('BBBPlugin') }}{{ 'Course'|get_lang }}{{ 'Session'|get_lang }}{{ 'Participants'|get_lang }}{{ 'Actions'|get_lang }}
    {{ meeting.created_at }}{{ meeting.created_at }} + {% if meeting.status == 1 %} + {{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }} + {% else %} + {{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }} + {% endif %} + + {% if meeting.record == 1 %} + {# Record list #} + {{ meeting.show_links }} + {% else %} + {{ 'NoRecording'|get_plugin_lang('BBBPlugin') }} + {% endif %} + {{ meeting.course ?: '-' }}{{ meeting.session ?: '-' }} + {{ meeting.participants ? meeting.participants|join('
    ') : '-' }} +
    + {{ meeting.action_links }} +
    diff --git a/plugin/justification/view/justification_user_list.tpl b/plugin/justification/view/justification_user_list.tpl new file mode 100644 index 000000000..35b88d286 --- /dev/null +++ b/plugin/justification/view/justification_user_list.tpl @@ -0,0 +1,38 @@ +{{ form }} + +{% if list %} +

    +{% endif %} diff --git a/plugin/justification/view/list.tpl b/plugin/justification/view/list.tpl new file mode 100644 index 000000000..2147efa94 --- /dev/null +++ b/plugin/justification/view/list.tpl @@ -0,0 +1,33 @@ +
    +
    + + + + + + + + + + {% for item in list %} + + + + + + + + {% endfor %} +
    {{ 'Name'| get_lang }}{{ 'ValidityDuration'| get_plugin_lang('Justification') }}{{ 'DateManualOn'| get_plugin_lang('Justification') }}{{ 'Actions'| get_lang }}
    {{ item.name }} ({{ item.code }}){{ item.validity_duration }}{{ item.date_manual_on }} + + {{'Edit' | get_lang}} + + + + {{'Delete' | get_lang}} + +
    +
    +
    diff --git a/plugin/kannelsms/README.md b/plugin/kannelsms/README.md new file mode 100644 index 000000000..0ca15db7a --- /dev/null +++ b/plugin/kannelsms/README.md @@ -0,0 +1,10 @@ +Kannel SMS plugin +=== + +This plugin enables Chamilo to send SMS messages to Chamilo users when a notification email is sent. + +SMS message types (a new course is created, an account is approved and so on) can be enabled or disabled from its plugin configuration option. + +Kannel SMS requires a Kannel server (software) to be installed on the machine and a cellphone to be connected to this server (usually through a USB cable). The communication is thus made through the cellphone itself. + +This is only for one-way communication from the Chamilo server to students cellphones. diff --git a/plugin/kannelsms/config.php b/plugin/kannelsms/config.php new file mode 100644 index 000000000..4b175b28b --- /dev/null +++ b/plugin/kannelsms/config.php @@ -0,0 +1,15 @@ + + */ +require_once __DIR__.'/../../main/inc/global.inc.php'; + +require_once 'lib/kannelsms.lib.php'; +require_once 'vendor/kannelsms_api.php'; +require_once 'lib/kannelsms_plugin.class.php'; diff --git a/plugin/kannelsms/index.php b/plugin/kannelsms/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/kannelsms/index.php @@ -0,0 +1 @@ + + */ +/** + * Initialization. + */ +require_once __DIR__.'/config.php'; +KannelsmsPlugin::create()->install(); diff --git a/plugin/kannelsms/lang/english.php b/plugin/kannelsms/lang/english.php new file mode 100644 index 000000000..d030d99eb --- /dev/null +++ b/plugin/kannelsms/lang/english.php @@ -0,0 +1,105 @@ +"; + +$strings['hostAddress'] = "Host address"; +$strings['hostAddress_help'] = "This is the address where your SMS gateway is hosted. Example: http://mobilegateway.com/tools/gateways/smssender"; +$strings['username'] = "Username"; +$strings['password'] = "Password"; +$strings['from'] = "Sender mobile number"; + +$strings['sms_types'] = "Send SMS when"; +$strings['mobile_phone_number'] = "Mobile"; + +$strings['MessageWelcomeXLoginXPasswordX'] = "A user is added to the platform"; +$strings['MessageXNewFileSharedCourseXByX'] = "A file is added to dropbox"; +$strings['MessageXAccountApprovedConnectX'] = "An account is approved"; +$strings['MessageXNewCourseXBeenCreatedX'] = "A new course is created"; +$strings['MessageXNewUserXSubscribedCourseX'] = "A user is subscribed to a course"; +$strings['MessageXNewCourseSuggestedTeacherX'] = "A new course is suggested"; +$strings['MessageXCourseOpeningRequestCodeXRegistered'] = "A new course opening request is registered"; +$strings['MessageXCourseOpeningRequestCourseCodeXApproved'] = "A new course opening request is approved"; +$strings['MessageXRequestOpenCourseCodeXReject'] = "A new course opening request is rejected"; +$strings['MessageXCourseOpeningRequestCourseCodeX'] = "Additional information is required for a new course opening request"; +$strings['MessageXBeenSubscribedCourseX'] = "A user is added to the course but not registered to it yet"; +$strings['MessageXAssignmentBeenCreatedCourseX'] = "Homework is created"; +$strings['MessageXAccountCreatedUpdatedLoginXPasswordX'] = "A user account is created or updated"; +$strings['MessageXPasswordUpdatedLoginXPasswordX'] = "A user password is updated"; +$strings['MessageXRequestedPasswordChange'] = "A user requests a password change"; +$strings['MessageXReceivedNewPersonalMessages'] = "New personal messages are received in user's inbox or forum"; +$strings['MessageXNewUserXPendingApproval'] = "A new user registration is pending for approval"; +$strings['MessageXXPostedForumXCourseX'] = "A user posts in forum"; +$strings['MessageXXXCheckEmailConnectMoreInfo'] = "An announcement is sent"; +$strings['MessageXXStudentXAnsweredTestX'] = "A student solves an exercise"; +$strings['MessageXXStudentXAnsweredTestXOpenQuestion'] = "A student solves an open question"; +$strings['MessageXXStudentXAnsweredTestXVoiceQuestion'] = "A student solves a voice question"; +$strings['MessageXXAnswerOpenQuestionTestXReviewed'] = "An open question answer is reviewed"; +$strings['MessageXXNewThreadXStartedForumX'] = "A new thread is started in forum"; +$strings['MessageXXNewAnswerPostedXForumX'] = "A new answer is posted in forum"; +$strings['MessageXXNewSystemAnnouncementAdded'] = "A new system announcement is added and the user does not want to send a test email"; +$strings['MessageXTestXNewSystemAnnouncementAdded'] = "A new system announcement is added and the user wants to send a test email"; +$strings['MessageXXSystemAnnouncementUpdate'] = "A new system announcement is updated and the user does not want to send a test email"; +$strings['MessageXTestXSystemAnnouncementUpdate'] = "A new system announcement is updated and the user wants to send a test email"; +$strings['MessageXUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "An assignment is uploaded and there is no current session (this will be sent only to coaches)"; +$strings['MessageXUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "An assignment is uploaded (this will be sent only to teachers)"; +$strings['MessageXUserXUploadedAssignmentXCourseX'] = "... and there is no current session (this will be sent only to coaches)"; +$strings['MessageXUserXUploadedAssignmentXCheckX'] = "... (this will be sent only to teachers)"; +$strings['MessageXSubscribedSessionX'] = "A session is created or updated and new users are subscribed"; +$strings['MessageXSubscribedSessionXCSV'] = "New users are subscribed to a session importing a CSV file"; +$strings['MessageXUserXSuggestedBeFriends'] = "A friendship invitation is received"; +$strings['MessageXUserXAnsweredInboxMessage'] = "A message reply is received in inbox"; +$strings['MessageXBeenInvitedJoinGroupX'] = "A group join invitation is received"; +$strings['MessageXMessagesSentEditedGroupXEdited'] = "A group topic message is updated"; +$strings['MessageXMessagesSentEditedGroupXAdded'] = "A group topic message is added"; +$strings['MessageXBeenInvitedCompleteSurveyXCourseX'] = "A new survey is created"; +$strings['MessageXReminderAssignmentXCourseXDue'] = "A reminder is sent to students who have not uploaded their assignment yet"; +$strings['MessageXUserDetailsModified'] = "User details have been updated"; + +$strings['WelcomeXLoginXPasswordX'] = "Welcome to %s. Here are your user account details: login: %s, password: %s"; +$strings['XNewFileSharedCourseXByX'] = "%s: A new file has been shared with you in course %s by %s"; +$strings['XAccountApprovedConnectX'] = "%s: Your account has been approved by an administrator. You can now connect to %s"; +$strings['XNewCourseXBeenCreatedX'] = "%s: A new course (%s) has been created by %s"; +$strings['XNewUserXSubscribedCourseX'] = "%s: A new user (%s) has just subscribed to your course (%s)"; +$strings['XNewCourseSuggestedTeacherX'] = "%s: A new course has been suggested by teacher (%s). Please connect to the admin page to review it"; +$strings['XCourseOpeningRequestCodeXRegistered'] = "%s: Your course opening request (code %s) has been registered. You will be informed when it is approved"; +$strings['XCourseOpeningRequestCourseCodeXApproved'] = "%s: Your course opening request for course code %s has been approved. Connect to your portal to use it"; +$strings['XRequestOpenCourseCodeXReject'] = "%s: We are sorry to inform you that your request to open course code %s has been rejected"; +$strings['XCourseOpeningRequestCourseCodeX'] = "%s: Your course opening request for course code %s requires more information. Check e-mail for more info"; +$strings['XBeenSubscribedCourseX'] = "%s: You have been subscribed to course %s"; +$strings['XAssignmentBeenCreatedCourseX'] = "%s: A new assignment has been created in course %s. Please connect for more info"; +$strings['XAccountCreatedUpdatedLoginXPasswordX'] = "%s: Your account has been created or updated. You can now login as %s, password: %s"; +$strings['XPasswordUpdatedLoginXPasswordX'] = "%s: Your password has been updated. You can now login as %s, password: %s"; +$strings['XRequestedPasswordChange'] = "%s: You have requested a password change. Please check your e-mail for more information"; +$strings['XReceivedNewPersonalMessages'] = "%s: You have received new personal messages in your inbox or in the forum. Please check your e-mail for more info"; +$strings['XNewUserXPendingApproval'] = "%s: A new user (%s) registration is pending your approval. Please connect to the admin panel to review"; +$strings['XXPostedForumXCourseX'] = "%s: %s just posted on forum %s in course %s"; +$strings['XXXCheckEmailConnectMoreInfo'] = "%s: %s: %s. Check your e-mail or connect for more info"; +$strings['XXStudentXAnsweredTestX'] = "%s: %s: Student %s has just answered test %s"; +$strings['XXStudentXAnsweredTestXOpenQuestion'] = "%s: %s: Student %s answered test %s's open question. Please connect to review"; +$strings['XXStudentXAnsweredTestXVoiceQuestion'] = "%s: %s: Student %s answered test %s's voice question. Please connect to review"; +$strings['XXAnswerOpenQuestionTestXReviewed'] = "%s: %s: Your answer to an open question in test %s has been reviewed"; +$strings['XXNewThreadXStartedForumX'] = "%s: %s: A new thread (%s) has been started in forum %s"; +$strings['XXNewAnswerPostedXForumX'] = "%s: %s: A new answer has been posted to (%s) in forum %s"; +$strings['XXNewSystemAnnouncementAdded'] = "%s: %s"; +$strings['XTestXNewSystemAnnouncementAdded'] = "%s (test): %s"; +$strings['XXSystemAnnouncementUpdate'] = "%s: %s (update)"; +$strings['XTestXSystemAnnouncementUpdate'] = "%s (test): %s"; +$strings['XUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "%s: User %s uploaded assignment %s in course %s"; +$strings['XUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "%s: User %s uploaded assignment %s. Check %s"; +$strings['XUserXUploadedAssignmentXCourseX'] = "%s: User %s uploaded assignment %s in course %s"; +$strings['XUserXUploadedAssignmentXCheckX'] = "%s: User %s uploaded assignment %s. Check %s"; +$strings['XSubscribedSessionX'] = "%s: You have been subscribed to session %s"; +$strings['XSubscribedSessionXCSV'] = "%s: You have been subscribed to session %s"; +$strings['XUserXSuggestedBeFriends'] = "%s: User %s suggested you should be friends. Check your inbox or e-mail for details"; +$strings['XUserXAnsweredInboxMessage'] = "%s: User %s just answered your Inbox message"; +$strings['XBeenInvitedJoinGroupX'] = "%s: you have been invited to join group %s. Check your Inbox for details"; +$strings['XMessagesSentEditedGroupXEdited'] = "%s: Messages sent or edited in group %s. Check your Inbox for details"; +$strings['XMessagesSentEditedGroupXAdded'] = "%s: Messages sent or edited in group %s. Check your Inbox for details"; +$strings['XBeenInvitedCompleteSurveyXCourseX'] = "%s: You have been invited to complete survey %s in course %s"; +$strings['XReminderAssignmentXCourseXDue'] = "%s: This is a simple reminder that your assignment (%s) in course %s is still due"; +$strings['XUserDetailsModified'] = "%s: Your user details have been modified. Please check your e-mail for more information"; diff --git a/plugin/kannelsms/lang/spanish.php b/plugin/kannelsms/lang/spanish.php new file mode 100644 index 000000000..eefe9fbfa --- /dev/null +++ b/plugin/kannelsms/lang/spanish.php @@ -0,0 +1,105 @@ +"; + +$strings['hostAddress'] = "Dirección del servidor"; +$strings['hostAddress_help'] = "Ésta es la dirección en donde se aloja su pasarela de SMS. Ejemplo: http://mobilegateway.com/tools/gateways/smssender"; +$strings['username'] = "Nombre de usuario"; +$strings['password'] = "Contraseña"; +$strings['from'] = "Número de móvil remitente"; + +$strings['sms_types'] = "Enviar un SMS cuando"; +$strings['mobile_phone_number'] = "Móvil"; + +$strings['MessageWelcomeXLoginXPasswordX'] = "Se añada un usuario a la plataforma"; +$strings['MessageXNewFileSharedCourseXByX'] = "Un archivo se añada a dropbox"; +$strings['MessageXAccountApprovedConnectX'] = "Se apruebe una cuenta de usuario"; +$strings['MessageXNewCourseXBeenCreatedX'] = "Se cree un curso nuevo"; +$strings['MessageXNewUserXSubscribedCourseX'] = "Un usuario se suscriba a un curso"; +$strings['MessageXNewCourseSuggestedTeacherX'] = "Un profesor sugiera un curso nuevo"; +$strings['MessageXCourseOpeningRequestCodeXRegistered'] = "Se registre una solicitud de apertura de curso"; +$strings['MessageXCourseOpeningRequestCourseCodeXApproved'] = "Se apruebe una solicitud de apertura de curso"; +$strings['MessageXRequestOpenCourseCodeXReject'] = "Se rechace una solicitud de apertura de curso"; +$strings['MessageXCourseOpeningRequestCourseCodeX'] = "Se requiera más información de una solicitud de apertura de curso"; +$strings['MessageXBeenSubscribedCourseX'] = "Se le suscriba a un curso a un usuario que aún no está registrado"; +$strings['MessageXAssignmentBeenCreatedCourseX'] = "Se cree una nueva tarea en un curso"; +$strings['MessageXAccountCreatedUpdatedLoginXPasswordX'] = "Se cree o actualice una cuenta de usuario"; +$strings['MessageXPasswordUpdatedLoginXPasswordX'] = "Se actualice la contraseña de un usuario"; +$strings['MessageXRequestedPasswordChange'] = "Un usuario solicite un cambio de contraseña"; +$strings['MessageXReceivedNewPersonalMessages'] = "Se reciban mensajes personales nuevos en la bandeja de entrada o en el foro"; +$strings['MessageXNewUserXPendingApproval'] = "Una inscripción de un nuevo usuario esté pendiente de aprobación"; +$strings['MessageXXPostedForumXCourseX'] = "Un usuario escriba en el foro"; +$strings['MessageXXXCheckEmailConnectMoreInfo'] = "Se envíe una notificación"; +$strings['MessageXXStudentXAnsweredTestX'] = "Un estudiante resuelva un ejercicio de un examen"; +$strings['MessageXXStudentXAnsweredTestXOpenQuestion'] = "Un estudiante conteste a una pregunta abierta de un examen"; +$strings['MessageXXStudentXAnsweredTestXVoiceQuestion'] = "Un estudiante conteste a una pregunta de voz de un examen"; +$strings['MessageXXAnswerOpenQuestionTestXReviewed'] = "Se revise una pregunta abierta de un examen"; +$strings['MessageXXNewThreadXStartedForumX'] = "Se cree un hilo nuevo en el foro"; +$strings['MessageXXNewAnswerPostedXForumX'] = "Se escriba una respuesta nueva en el foro"; +$strings['MessageXXNewSystemAnnouncementAdded'] = "Se añada una notificación y el usuario no desee enviar un examen por correo electrónico"; +$strings['MessageXTestXNewSystemAnnouncementAdded'] = "Se añada una notificación y el usuario desee enviar un examen por correo electrónico"; +$strings['MessageXXSystemAnnouncementUpdate'] = "Se actualice una notificación y el usuario no desee enviar un examen por correo electrónico"; +$strings['MessageXTestXSystemAnnouncementUpdate'] = "Se actualice una notificación y el usuario desee enviar un examen por correo electrónico"; +$strings['MessageXUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "Un estudiante suba su tarea y no exista una sesión (se enviará sólo a coordinadores)"; +$strings['MessageXUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "Un estudiante suba su tarea (se enviará sólo a profesores)"; +$strings['MessageXUserXUploadedAssignmentXCourseX'] = "... y no exista una sesión (se enviará sólo a coordinadores)"; +$strings['MessageXUserXUploadedAssignmentXCheckX'] = "... (se enviará sólo a profesores)"; +$strings['MessageXSubscribedSessionX'] = "Se cree o edite una sesión nueva y se suscriban a usuarios nuevos"; +$strings['MessageXSubscribedSessionXCSV'] = "Se suscriban a usuarios nuevos desde un archivo CSV"; +$strings['MessageXUserXSuggestedBeFriends'] = "Se reciba una invitación de amistad"; +$strings['MessageXUserXAnsweredInboxMessage'] = "Se reciba la contestación a un mensaje en la bandeja de entrada"; +$strings['MessageXBeenInvitedJoinGroupX'] = "Se reciba una invitación para unirse a un grupo"; +$strings['MessageXMessagesSentEditedGroupXEdited'] = "Se actualice un mensaje en un tema de grupo"; +$strings['MessageXMessagesSentEditedGroupXAdded'] = "Se añada un mensaje en un tema de grupo"; +$strings['MessageXBeenInvitedCompleteSurveyXCourseX'] = "Se cree una encuesta nueva"; +$strings['MessageXReminderAssignmentXCourseXDue'] = "Se envíe un recordatorio a los estudiantes que no hayan enviado su tarea"; +$strings['MessageXUserDetailsModified'] = "Se actualice información de un usuario"; + +$strings['WelcomeXLoginXPasswordX'] = "Bienvenido a %s. Éstos son sus datos de acceso: usuario: %s, contraseña: %s"; +$strings['XNewFileSharedCourseXByX'] = "%s: Un nuevo archivo ha sido compartido contigo en el curso %s por %s"; +$strings['XAccountApprovedConnectX'] = "%s: Su cuenta ha sido aprobada por un administrador. Ya puede conectarse a %s"; +$strings['XNewCourseXBeenCreatedX'] = "%s: Un nuevo curso (%s) ha sido creado por %s"; +$strings['XNewUserXSubscribedCourseX'] = "%s: Un nuevo usuario (%s) se ha suscrito a su curso (%s)"; +$strings['XNewCourseSuggestedTeacherX'] = "%s: Un nuevo curso ha sido sugerido por el profesor (%s). Por favor, conéctese a la página de administración para revisarlo"; +$strings['XCourseOpeningRequestCodeXRegistered'] = "%s: Su solicitud de apertura de curso (código %s) ha sido registrada. Será informado cuando ésta sea aprobada"; +$strings['XCourseOpeningRequestCourseCodeXApproved'] = "%s: Su solicitud de apertura de curso para el código de curso %s ha sido aprobada. Conéctese a su portal para usarlo"; +$strings['XRequestOpenCourseCodeXReject'] = "%s: Lamentamos informarle que su solicitud de apertura de curso para el código de curso %s ha sido rechazada"; +$strings['XCourseOpeningRequestCourseCodeX'] = "%s: Su solicitud de apertura de curso para el código de curso %s requiere más información. Compruebe su e-mail para más información"; +$strings['XBeenSubscribedCourseX'] = "%s: Ha sido suscrito al curso %s"; +$strings['XAssignmentBeenCreatedCourseX'] = "%s: Una nueva tarea ha sido creada en el curso %s. Por favor, conéctese para más información"; +$strings['XAccountCreatedUpdatedLoginXPasswordX'] = "%s: Su cuenta ha sido creada o actualizada. Ya puede conectarse como %s, contraseña: %s"; +$strings['XPasswordUpdatedLoginXPasswordX'] = "%s: Su contraseña ha sido actualizada. Ya puede conectarse como %s, contraseña: %s"; +$strings['XRequestedPasswordChange'] = "%s: Ha solicitado un cambio de contraseña. Por favor, compruebe su e-mail para más información"; +$strings['XReceivedNewPersonalMessages'] = "%s: Ha recibido mensajes personales nuevos en su bandeja de entrada o en el foro. Por favor, compruebe su e-mail para más información"; +$strings['XNewUserXPendingApproval'] = "%s: Una inscripción de un nuevo usuario (%s) está pendiente de su aprobación. Por favor, conéctese al panel de administración para revisarla"; +$strings['XXPostedForumXCourseX'] = "%s: %s acaba de escribir en el foro %s del curso %s"; +$strings['XXXCheckEmailConnectMoreInfo'] = "%s: %s: %s. Compruebe su e-mail o conéctese para más información"; +$strings['XXStudentXAnsweredTestX'] = "%s: %s: El estudiante %s acaba de contestar al test %s"; +$strings['XXStudentXAnsweredTestXOpenQuestion'] = "%s: %s: El estudiante %s contestó a la pregunta abierta del test %s. Por favor, conéctese para revisarla"; +$strings['XXStudentXAnsweredTestXVoiceQuestion'] = "%s: %s: El estudiante %s contestó a la pregunta de voz del test %s. Por favor, conéctese para revisarla"; +$strings['XXAnswerOpenQuestionTestXReviewed'] = "%s: %s: Su respuesta a la pregunta abierta en el test %s ha sido revisada."; +$strings['XXNewThreadXStartedForumX'] = "%s: %s: Se ha comenzado un nuevo hilo (%s) en el foro %s"; +$strings['XXNewAnswerPostedXForumX'] = "%s: %s: Se ha escrito una nueva respuesta a (%s) en el foro %s"; +$strings['XXNewSystemAnnouncementAdded'] = "%s: %s"; +$strings['XTestXNewSystemAnnouncementAdded'] = "%s (prueba): %s"; +$strings['XXSystemAnnouncementUpdate'] = "%s: %s (actualización)"; +$strings['XTestXSystemAnnouncementUpdate'] = "%s (prueba): %s"; +$strings['XUserXUploadedAssignmentXCourseXStudentSubmitsPaper'] = "%s: El usuario %s subió la tarea %s al curso %s"; +$strings['XUserXUploadedAssignmentXCheckXStudentSubmitsPaper'] = "%s: El usuario %s subió la tarea %s. Compruébelo en %s"; +$strings['XUserXUploadedAssignmentXCourseX'] = "%s: El usuario %s subió la tarea %s al curso %s"; +$strings['XUserXUploadedAssignmentXCheckX'] = "%s: El usuario %s subió la tarea %s. Compruébelo en %s"; +$strings['XSubscribedSessionX'] = "%s: Ha sido suscrito a la sesión %s"; +$strings['XSubscribedSessionXCSV'] = "%s: Ha sido suscrito a la sesión %s"; +$strings['XUserXSuggestedBeFriends'] = "%s: El usuario %s le sugirió que podrían ser amigos. Compruebe su bandeja de entrada o su e-mail para más detalles"; +$strings['XUserXAnsweredInboxMessage'] = "%s: El usuario acaba de contestar a su mensaje de la bandeja de entrada"; +$strings['XBeenInvitedJoinGroupX'] = "%s: Ha sido invitado a unirse al grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XMessagesSentEditedGroupXEdited'] = "%s: Hay mensajes enviados o editados en el grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XMessagesSentEditedGroupXAdded'] = "%s: Hay mensajes enviados o editados en el grupo %s. Compruebe su bandeja de entrada para más detalles"; +$strings['XBeenInvitedCompleteSurveyXCourseX'] = "%s: Ha sido invitado a completar la encuesta %s del curso %s"; +$strings['XReminderAssignmentXCourseXDue'] = "%s: Se le recuerda que su tarea (%s) del curso %s aún está pendiente"; +$strings['XUserDetailsModified'] = "%s: Su información de usuario ha sido modificada. Compruebe su e-mail para más información"; diff --git a/plugin/kannelsms/lib/kannelsms.lib.php b/plugin/kannelsms/lib/kannelsms.lib.php new file mode 100644 index 000000000..83a5dcd7b --- /dev/null +++ b/plugin/kannelsms/lib/kannelsms.lib.php @@ -0,0 +1,661 @@ + + * + * Kannelsms-Chamilo connector class + */ +class Kannelsms implements SmsPluginLibraryInterface +{ + public $api; + public $hostAddress; + public $username; + public $password; + public $from; + public $plugin_enabled = false; + + /** + * Constructor (generates a connection to the API). + * + * @param string Kannelsms API key required to use the plugin + */ + public function __construct($apiKey = null) + { + $plugin = KannelsmsPlugin::create(); + $kannelSMSPlugin = $plugin->get('tool_enable'); + $this->table = Database::get_main_table('user_field_values'); + if ($kannelSMSPlugin == true) { + $this->api = new Kannel(' '); + $this->hostAddress = $plugin->get('hostAddress'); + $this->username = $plugin->get('username'); + $this->password = $plugin->get('password'); + $this->from = $plugin->get('from'); + $this->plugin_enabled = true; + } + } + + /** + * getMobilePhoneNumberById (retrieves a user mobile phone number by user id). + * + * @param int $userId User id + * + * @return int User's mobile phone number + */ + public function getMobilePhoneNumberById($userId) + { + $mobilePhoneNumberExtraField = new ExtraField('user'); + $mobilePhoneNumberExtraField = $mobilePhoneNumberExtraField->get_handler_field_info_by_field_variable( + 'mobile_phone_number' + ); + + $mobilePhoneNumberExtraFieldValue = new ExtraFieldValue('user'); + $mobilePhoneNumberExtraFieldValue = $mobilePhoneNumberExtraFieldValue->get_values_by_handler_and_field_id( + $userId, + $mobilePhoneNumberExtraField['id'] + ); + + return $mobilePhoneNumberExtraFieldValue['value']; + } + + /** + * send (sends an SMS to the user). + * + * @param array Data needed to send the SMS. It is mandatory to include the + * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least. + * More data may be neccesary depending on the message type + * Example: $additional_parameters = array( + * 'smsType' => EXAMPLE_SMS_TYPE, + * 'userId' => $userId, + * 'moreData' => $moreData + * ); + */ + public function send($additionalParameters) + { + $trimmedKey = trim(CONFIG_SECURITY_API_KEY); + if (!empty($trimmedKey)) { + $message = [ + "to" => array_key_exists("mobilePhoneNumber", $additionalParameters) ? + $additionalParameters['mobilePhoneNumber'] : $this->getMobilePhoneNumberById($additionalParameters['userId']), + "message" => $this->getSms($additionalParameters), + ]; + + if (!empty($message['message'])) { + if (extension_loaded('curl')) { + $url = $this->hostAddress.'?username='. + $this->username.'&password='.$this->password.'&from='. + $this->from.'&to='.$message['to'].'&msg='.urlencode($message['message']); + $ch = curl_init($url); + curl_exec($ch); + curl_close($ch); + } + + // Commented for future message logging / tracking purposes + /*if( $result["success"] ) { + echo "Message sent - ID: " . $result["id"]; + } else { + echo "Message failed - Error: " . $result["error_message"]; + }*/ + } + } + } + + /** + * buildSms (builds an SMS from a template and data). + * + * @param object KannelsmsPlugin object + * @param object Template object + * @param string Template file name + * @param string Text key from lang file + * @param array Data to fill message variables (if any) + * + * @return object Template object with message property updated + */ + public function buildSms($plugin, $tpl, $templateName, $messageKey, $parameters = null) + { + $result = Database::select( + 'selected_value', + 'settings_current', + [ + 'where' => ['variable = ?' => ['kannelsms_message'.$messageKey]], + ] + ); + + if (empty($result)) { + $tpl->assign('message', ''); + } else { + $templatePath = 'kannelsms/sms_templates/'; + $content = $tpl->fetch($templatePath.$templateName); + $message = $plugin->get_lang($messageKey); + if ($parameters !== null) { + $message = vsprintf($message, $parameters); + } + $tpl->assign('message', $message); + } + + return $tpl->params['message']; + } + + /** + * getSms (returns an SMS message depending of its type). + * + * @param array Data needed to send the SMS. It is mandatory to include the + * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least. + * More data may be neccesary depending on the message type + * Example: $additional_parameters = array( + * 'smsType' => EXAMPLE_SMS_TYPE, + * 'userId' => $userId, + * 'moreData' => $moreData + * ); + * + * @return string A ready to be sent SMS + */ + public function getSms($additionalParameters) + { + $plugin = KannelsmsPlugin::create(); + $tool_name = $plugin->get_lang('plugin_title'); + $tpl = new Template($tool_name); + + switch ($additionalParameters['smsType']) { + case SmsPlugin::WELCOME_LOGIN_PASSWORD: + $userInfo = api_get_user_info($additionalParameters['userId']); + + return $this->buildSms( + $plugin, + $tpl, + 'welcome_login_password.tpl', + 'WelcomeXLoginXPasswordX', + [ + api_get_setting('siteName'), + $userInfo['username'], + $additionalParameters['password'], + ] + ); + break; + case SmsPlugin::NEW_FILE_SHARED_COURSE_BY: + return $this->buildSms( + $plugin, + $tpl, + 'new_file_shared_course_by.tpl', + 'XNewFileSharedCourseXByX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + $additionalParameters['userUsername'], + ] + ); + break; + case SmsPlugin::ACCOUNT_APPROVED_CONNECT: + return $this->buildSms( + $plugin, + $tpl, + 'account_approved_connect.tpl', + 'XAccountApprovedConnectX', + [ + api_get_setting('siteName'), + $tpl->params['_p']['web'], + ] + ); + break; + case SmsPlugin::NEW_COURSE_BEEN_CREATED: + return $this->buildSms( + $plugin, + $tpl, + 'new_course_been_created.tpl', + 'XNewCourseXBeenCreatedX', + [ + api_get_setting('siteName'), + $additionalParameters['courseName'], + $additionalParameters['creatorUsername'], + ] + ); + break; + case SmsPlugin::NEW_USER_SUBSCRIBED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'new_user_subscribed_course.tpl', + 'XNewUserXSubscribedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['userUsername'], + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::NEW_COURSE_SUGGESTED_TEACHER: + return $this->buildSms( + $plugin, + $tpl, + 'new_course_suggested_teacher.tpl', + 'XNewCourseSuggestedTeacherX', + [ + api_get_setting('siteName'), + $additionalParameters['userUsername'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REGISTERED: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_code_registered.tpl', + 'XCourseOpeningRequestCodeXRegistered', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_APPROVED: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_course_code_approved.tpl', + 'XCourseOpeningRequestCourseCodeXApproved', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REJECTED: + return $this->buildSms( + $plugin, + $tpl, + 'request_open_course_code_rejected.tpl', + 'XRequestOpenCourseCodeXReject', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::COURSE_OPENING_REQUEST_CODE: + return $this->buildSms( + $plugin, + $tpl, + 'course_opening_request_course_code.tpl', + 'XCourseOpeningRequestCourseCodeX', + [ + api_get_setting('siteName'), + $additionalParameters['courseCode'], + ] + ); + break; + case SmsPlugin::BEEN_SUBSCRIBED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'been_subscribed_course.tpl', + 'XBeenSubscribedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + ] + ); + break; + case SmsPlugin::ASSIGNMENT_BEEN_CREATED_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'assignment_been_created_course.tpl', + 'XAssignmentBeenCreatedCourseX', + [ + api_get_setting('siteName'), + $additionalParameters['courseTitle'], + ] + ); + break; + // Message types to be implemented. Fill the array parameter with arguments. + /*case SmsPlugin::ACCOUNT_CREATED_UPDATED_LOGIN_PASSWORD: + return $this->buildSms( + $plugin, + $tpl, + 'account_created_updated_login_password.tpl', + 'XAccountCreatedUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::PASSWORD_UPDATED_LOGIN_PASSWORD: + return $this->buildSms( + $plugin, + $tpl, + 'password_updated_login_password.tpl', + 'XPasswordUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::REQUESTED_PASSWORD_CHANGE: + return $this->buildSms( + $plugin, + $tpl, + 'requested_password_change.tpl', + 'XPasswordUpdatedLoginXPasswordX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::RECEIVED_NEW_PERSONAL_MESSAGES: + return $this->buildSms( + $plugin, + $tpl, + 'received_new_personal_messages.tpl', + 'XReceivedNewPersonalMessages', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_USER_PENDING_APPROVAL: + return $this->buildSms( + $plugin, + $tpl, + 'new_user_pending_approval.tpl', + 'XNewUserXPendingApproval', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::POSTED_FORUM_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'posted_forum_course.tpl', + 'XXPostedForumXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::CHECK_EMAIL_CONNECT_MORE_INFO: + return $this->buildSms( + $plugin, + $tpl, + 'check_email_connect_more_info.tpl', + 'XXXCheckEmailConnectMoreInfo', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test.tpl', + 'XXStudentXAnsweredTestX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST_OPEN_QUESTION: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test_open_question.tpl', + 'XXStudentXAnsweredTestXOpenQuestion', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::STUDENT_ANSWERED_TEST_VOICE_QUESTION: + return $this->buildSms( + $plugin, + $tpl, + 'student_answered_test_voice_question.tpl', + 'XXStudentXAnsweredTestXVoiceQuestion', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::ANSWER_OPEN_QUESTION_TEST_REVIEWED: + return $this->buildSms( + $plugin, + $tpl, + 'answer_open_question_test_reviewed.tpl', + 'XXAnswerOpenQuestionTestXReviewed', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_THREAD_STARTED_FORUM: + return $this->buildSms( + $plugin, + $tpl, + 'new_thread_started_forum.tpl', + 'XXNewThreadXStartedForumX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_ANSWER_POSTED_FORUM: + return $this->buildSms( + $plugin, + $tpl, + 'new_answer_posted_forum.tpl', + 'XXNewAnswerPostedXForumX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::NEW_SYSTEM_ANNOUNCEMENT_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'new_system_announcement_added.tpl', + 'XXNewSystemAnnouncementAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::TEST_NEW_SYSTEM_ANNOUNCEMENT_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'test_new_system_announcement_added.tpl', + 'XTestXNewSystemAnnouncementAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SYSTEM_ANNOUNCEMENT_UPDATE: + return $this->buildSms( + $plugin, + $tpl, + 'system_announcement_update.tpl', + 'XXSystemAnnouncementUpdate', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::TEST_SYSTEM_ANNOUNCEMENT_UPDATE: + return $this->buildSms( + $plugin, + $tpl, + 'test_system_announcement_update.tpl', + 'XXSystemAnnouncementUpdate', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE_STUDENT_SUBMITS_PAPER: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_course_student_submits_paper.tpl', + 'XUserXUploadedAssignmentXCourseXStudentSubmitsPaper', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK_STUDENT_SUBMITS_PAPER: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_check_student_submits_paper.tpl', + 'XUserXUploadedAssignmentXCheckXStudentSubmitsPaper', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_course.tpl', + 'XUserXUploadedAssignmentXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK: + return $this->buildSms( + $plugin, + $tpl, + 'user_uploaded_assignment_check.tpl', + 'XUserXUploadedAssignmentXCheckX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SUBSCRIBED_SESSION: + return $this->buildSms( + $plugin, + $tpl, + 'subscribed_session.tpl', + 'XSubscribedSessionX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::SUBSCRIBED_SESSION_CSV: + return $this->buildSms( + $plugin, + $tpl, + 'subscribed_session_csv.tpl', + 'XSubscribedSessionXCSV', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_SUGGESTED_BE_FRIENDS: + return $this->buildSms( + $plugin, + $tpl, + 'user_suggested_be_friends.tpl', + 'XUserXSuggestedBeFriends', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_ANSWERED_INBOX_MESSAGE: + return $this->buildSms( + $plugin, + $tpl, + 'user_answered_inbox_message.tpl', + 'XUserXAnsweredInboxMessage', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::BEEN_INVITED_JOIN_GROUP: + return $this->buildSms( + $plugin, + $tpl, + 'been_invited_join_group.tpl', + 'XBeenInvitedJoinGroupX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_EDITED: + return $this->buildSms( + $plugin, + $tpl, + 'messages_sent_edited_group_edited.tpl', + 'XMessagesSentEditedGroupXEdited', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_ADDED: + return $this->buildSms( + $plugin, + $tpl, + 'messages_sent_edited_group_added.tpl', + 'XMessagesSentEditedGroupXAdded', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::BEEN_INVITED_COMPLETE_SURVEY_COURSE: + return $this->buildSms( + $plugin, + $tpl, + 'been_invited_complete_survey_course.tpl', + 'XBeenInvitedCompleteSurveyXCourseX', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::REMINDER_ASSIGNMENT_COURSE_DUE: + return $this->buildSms( + $plugin, + $tpl, + 'reminder_assignment_course_due.tpl', + 'XReminderAssignmentXCourseXDue', + array( + api_get_setting('siteName') + ) + ); + break;*/ + /*case SmsPlugin::USER_DETAILS_MODIFIED: + return $this->buildSms( + $plugin, + $tpl, + 'user_details_modified.tpl', + 'XUserDetailsModified', + array( + api_get_setting('siteName') + ) + ); + break;*/ + default: + return ''; + } + } +} diff --git a/plugin/kannelsms/lib/kannelsms_plugin.class.php b/plugin/kannelsms/lib/kannelsms_plugin.class.php new file mode 100644 index 000000000..33a3b0cd1 --- /dev/null +++ b/plugin/kannelsms/lib/kannelsms_plugin.class.php @@ -0,0 +1,56 @@ + + * @author Julio Montoya Refactor code + */ +class KannelsmsPlugin extends SmsPlugin +{ + /** + * Constructor. + */ + public function __construct() + { + $fields = [ + 'tool_enable' => 'boolean', + 'hostAddress' => 'text', + 'username' => 'text', + 'password' => 'text', + 'from' => 'text', + ]; + $smsTypeOptions = $this->getSmsTypeOptions(); + foreach ($smsTypeOptions as $smsTypeOption) { + $fields[$smsTypeOption] = 'checkbox'; + } + parent::__construct('0.1', 'Imanol Losada', $fields); + } + + /** + * create (a singleton function that ensures KannelsmsPlugin instance is + * created only once. If it is already created, it returns the instance). + * + * @return object KannelsmsPlugin instance + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * install (uninstalls the plugin and removes all plugin's tables and/or rows). + */ + public function uninstall() + { + $tSettings = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); + $sql = "DELETE FROM $tSettings WHERE subkey = 'kannelsms'"; + Database::query($sql); + } +} diff --git a/plugin/kannelsms/license.txt b/plugin/kannelsms/license.txt new file mode 100644 index 000000000..11e71ecff --- /dev/null +++ b/plugin/kannelsms/license.txt @@ -0,0 +1,5 @@ +This plugin, as the rest of Chamilo, is released under the GNU/GPLv3 license. + +The Kannel library included is provided under the ICS license, compatible +with GNU/GPLv3. See https://github.com/mediaburst/kannel-php/issues/3 for +details. \ No newline at end of file diff --git a/plugin/kannelsms/plugin.php b/plugin/kannelsms/plugin.php new file mode 100644 index 000000000..cb0459c2f --- /dev/null +++ b/plugin/kannelsms/plugin.php @@ -0,0 +1,11 @@ + + */ +require_once __DIR__.'/config.php'; + +$plugin_info = KannelsmsPlugin::create()->get_info(); diff --git a/plugin/kannelsms/sms_templates/account_approved_connect.tpl b/plugin/kannelsms/sms_templates/account_approved_connect.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/account_approved_connect.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/account_created_updated_login_password.tpl b/plugin/kannelsms/sms_templates/account_created_updated_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/account_created_updated_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/answer_open_question_test_reviewed.tpl b/plugin/kannelsms/sms_templates/answer_open_question_test_reviewed.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/answer_open_question_test_reviewed.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/assignment_been_created_course.tpl b/plugin/kannelsms/sms_templates/assignment_been_created_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/assignment_been_created_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/been_invited_complete_survey_course.tpl b/plugin/kannelsms/sms_templates/been_invited_complete_survey_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/been_invited_complete_survey_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/been_invited_join_group.tpl b/plugin/kannelsms/sms_templates/been_invited_join_group.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/been_invited_join_group.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/been_subscribed_course.tpl b/plugin/kannelsms/sms_templates/been_subscribed_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/been_subscribed_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/check_email_connect_more_info.tpl b/plugin/kannelsms/sms_templates/check_email_connect_more_info.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/check_email_connect_more_info.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/course_opening_request_code_registered.tpl b/plugin/kannelsms/sms_templates/course_opening_request_code_registered.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/course_opening_request_code_registered.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/course_opening_request_course_code.tpl b/plugin/kannelsms/sms_templates/course_opening_request_course_code.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/course_opening_request_course_code.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/course_opening_request_course_code_approved.tpl b/plugin/kannelsms/sms_templates/course_opening_request_course_code_approved.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/course_opening_request_course_code_approved.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/messages_sent_edited_group_added.tpl b/plugin/kannelsms/sms_templates/messages_sent_edited_group_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/messages_sent_edited_group_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/messages_sent_edited_group_edited.tpl b/plugin/kannelsms/sms_templates/messages_sent_edited_group_edited.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/messages_sent_edited_group_edited.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_answer_posted_forum.tpl b/plugin/kannelsms/sms_templates/new_answer_posted_forum.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_answer_posted_forum.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_course_been_created.tpl b/plugin/kannelsms/sms_templates/new_course_been_created.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_course_been_created.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_course_suggested_teacher.tpl b/plugin/kannelsms/sms_templates/new_course_suggested_teacher.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_course_suggested_teacher.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_file_shared_course_by.tpl b/plugin/kannelsms/sms_templates/new_file_shared_course_by.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_file_shared_course_by.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_system_announcement_added.tpl b/plugin/kannelsms/sms_templates/new_system_announcement_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_system_announcement_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_thread_started_forum.tpl b/plugin/kannelsms/sms_templates/new_thread_started_forum.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_thread_started_forum.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_user_pending_approval.tpl b/plugin/kannelsms/sms_templates/new_user_pending_approval.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_user_pending_approval.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/new_user_subscribed_course.tpl b/plugin/kannelsms/sms_templates/new_user_subscribed_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/new_user_subscribed_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/password_updated_login_password.tpl b/plugin/kannelsms/sms_templates/password_updated_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/password_updated_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/posted_forum_course.tpl b/plugin/kannelsms/sms_templates/posted_forum_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/posted_forum_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/received_new_personal_messages.tpl b/plugin/kannelsms/sms_templates/received_new_personal_messages.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/received_new_personal_messages.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/reminder_assignment_course_due.tpl b/plugin/kannelsms/sms_templates/reminder_assignment_course_due.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/reminder_assignment_course_due.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/request_open_course_code_rejected.tpl b/plugin/kannelsms/sms_templates/request_open_course_code_rejected.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/request_open_course_code_rejected.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/requested_password_change.tpl b/plugin/kannelsms/sms_templates/requested_password_change.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/requested_password_change.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/student_answered_test.tpl b/plugin/kannelsms/sms_templates/student_answered_test.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/student_answered_test.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/student_answered_test_open_question.tpl b/plugin/kannelsms/sms_templates/student_answered_test_open_question.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/student_answered_test_open_question.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/student_answered_test_voice_question.tpl b/plugin/kannelsms/sms_templates/student_answered_test_voice_question.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/student_answered_test_voice_question.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/subscribed_session.tpl b/plugin/kannelsms/sms_templates/subscribed_session.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/subscribed_session.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/subscribed_session_csv.tpl b/plugin/kannelsms/sms_templates/subscribed_session_csv.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/subscribed_session_csv.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/system_announcement_update.tpl b/plugin/kannelsms/sms_templates/system_announcement_update.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/system_announcement_update.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/test_new_system_announcement_added.tpl b/plugin/kannelsms/sms_templates/test_new_system_announcement_added.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/test_new_system_announcement_added.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/test_system_announcement_update.tpl b/plugin/kannelsms/sms_templates/test_system_announcement_update.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/test_system_announcement_update.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_answered_inbox_message.tpl b/plugin/kannelsms/sms_templates/user_answered_inbox_message.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_answered_inbox_message.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_details_modified.tpl b/plugin/kannelsms/sms_templates/user_details_modified.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_details_modified.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_suggested_be_friends.tpl b/plugin/kannelsms/sms_templates/user_suggested_be_friends.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_suggested_be_friends.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_uploaded_assignment_check.tpl b/plugin/kannelsms/sms_templates/user_uploaded_assignment_check.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_uploaded_assignment_check.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl b/plugin/kannelsms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_uploaded_assignment_check_student_submits_paper.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_uploaded_assignment_course.tpl b/plugin/kannelsms/sms_templates/user_uploaded_assignment_course.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_uploaded_assignment_course.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl b/plugin/kannelsms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/user_uploaded_assignment_course_student_submits_paper.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/sms_templates/welcome_login_password.tpl b/plugin/kannelsms/sms_templates/welcome_login_password.tpl new file mode 100644 index 000000000..414c17f26 --- /dev/null +++ b/plugin/kannelsms/sms_templates/welcome_login_password.tpl @@ -0,0 +1 @@ +{{ message }} diff --git a/plugin/kannelsms/uninstall.php b/plugin/kannelsms/uninstall.php new file mode 100644 index 000000000..2d3345145 --- /dev/null +++ b/plugin/kannelsms/uninstall.php @@ -0,0 +1,17 @@ + + */ +/** + * Queries. + */ +require_once __DIR__.'/config.php'; +KannelsmsPlugin::create()->uninstall(); diff --git a/plugin/kannelsms/vendor/changelog.md b/plugin/kannelsms/vendor/changelog.md new file mode 100644 index 000000000..9a1545b37 --- /dev/null +++ b/plugin/kannelsms/vendor/changelog.md @@ -0,0 +1,22 @@ +# Changelog + +## 1.0 (July 19th, 2012) + +* Initial release. [JI/MS] + +### 1.1 (August 21st, 2012) + +* Added /get_key functionality to translate a legacy Mediaburst username and password into a new Kannel API key. [JI] +* Deprecated `checkCredit()` and replaced with `checkBalance()` [JI] + +### 1.2 (September 7th, 2012) + +* Added various new Wordpress classes, including the Kannel_Plugin class for writing plugins based on Kannel. [JI] + +### 1.3 (September 18th, 2012) + +* Added `is_valid_msisdn()` method. [JI] + +#### 1.3.1 (November 13th, 2012) + +* Updated `is_valid_msisdn()` method to handle 9-digit phone numbers, e.g. Norway. [JI] \ No newline at end of file diff --git a/plugin/kannelsms/vendor/exception.php b/plugin/kannelsms/vendor/exception.php new file mode 100644 index 000000000..dae50bba3 --- /dev/null +++ b/plugin/kannelsms/vendor/exception.php @@ -0,0 +1,27 @@ +key = $key; + } + + $this->ssl = (array_key_exists('ssl', $options)) ? $options['ssl'] : null; + $this->proxy_host = (array_key_exists('proxy_host', $options)) ? $options['proxy_host'] : null; + $this->proxy_port = (array_key_exists('proxy_port', $options)) ? $options['proxy_port'] : null; + $this->from = (array_key_exists('from', $options)) ? $options['from'] : null; + $this->long = (array_key_exists('long', $options)) ? $options['long'] : null; + $this->truncate = (array_key_exists('truncate', $options)) ? $options['truncate'] : null; + $this->invalid_char_action = (array_key_exists('invalid_char_action', $options)) ? $options['invalid_char_action'] : null; + $this->log = (array_key_exists('log', $options)) ? $options['log'] : false; + } + + /** + * Send some text messages + * + * + * @author Martin Steel + */ + public function send(array $sms) { + if (!is_array($sms)) { + throw new KannelException("sms parameter must be an array"); + } + $single_message = $this->is_assoc($sms); + + if ($single_message) { + $sms = array($sms); + } + + $req_doc = new \DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Message'); + $req_doc->appendChild($root); + + $user_node = $req_doc->createElement('Key'); + $user_node->appendChild($req_doc->createTextNode($this->key)); + $root->appendChild($user_node); + + for ($i = 0; $i < count($sms); $i++) { + $single = $sms[$i]; + + $sms_node = $req_doc->createElement('SMS'); + + // Phone number + $sms_node->appendChild($req_doc->createElement('To', $single['to'])); + + // Message text + $content_node = $req_doc->createElement('Content'); + $content_node->appendChild($req_doc->createTextNode($single['message'])); + $sms_node->appendChild($content_node); + + // From + if (array_key_exists('from', $single) || isset($this->from)) { + $from_node = $req_doc->createElement('From'); + $from_node->appendChild($req_doc->createTextNode(array_key_exists('from', $single) ? $single['from'] : $this->from)); + $sms_node->appendChild($from_node); + } + + // Client ID + if (array_key_exists('client_id', $single)) { + $client_id_node = $req_doc->createElement('ClientID'); + $client_id_node->appendChild($req_doc->createTextNode($single['client_id'])); + $sms_node->appendChild($client_id_node); + } + + // Long + if (array_key_exists('long', $single) || isset($this->long)) { + $long = array_key_exists('long', $single) ? $single['long'] : $this->long; + $long_node = $req_doc->createElement('Long'); + $long_node->appendChild($req_doc->createTextNode($long ? 1 : 0)); + $sms_node->appendChild($long_node); + } + + // Truncate + if (array_key_exists('truncate', $single) || isset($this->truncate)) { + $truncate = array_key_exists('truncate', $single) ? $single['truncate'] : $this->truncate; + $trunc_node = $req_doc->createElement('Truncate'); + $trunc_node->appendChild($req_doc->createTextNode($truncate ? 1 : 0)); + $sms_node->appendChild($trunc_node); + } + + // Invalid Char Action + if (array_key_exists('invalid_char_action', $single) || isset($this->invalid_char_action)) { + $action = array_key_exists('invalid_char_action', $single) ? $single['invalid_char_action'] : $this->invalid_char_action; + switch (strtolower($action)) { + case 'error': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 1)); + break; + case 'remove': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 2)); + break; + case 'replace': + $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 3)); + break; + default: + break; + } + } + + // Wrapper ID + $sms_node->appendChild($req_doc->createElement('WrapperID', $i)); + + $root->appendChild($sms_node); + } + + $req_xml = $req_doc->saveXML(); + + $resp_xml = $this->postToKannel(self::API_SMS_METHOD, $req_xml); + $resp_doc = new \DOMDocument(); + $resp_doc->loadXML($resp_xml); + + $response = array(); + $err_no = null; + $err_desc = null; + + foreach($resp_doc->documentElement->childNodes AS $doc_child) { + switch(strtolower($doc_child->nodeName)) { + case 'sms_resp': + $resp = array(); + $wrapper_id = null; + foreach($doc_child->childNodes AS $resp_node) { + switch(strtolower($resp_node->nodeName)) { + case 'messageid': + $resp['id'] = $resp_node->nodeValue; + break; + case 'errno': + $resp['error_code'] = $resp_node->nodeValue; + break; + case 'errdesc': + $resp['error_message'] = $resp_node->nodeValue; + break; + case 'wrapperid': + $wrapper_id = $resp_node->nodeValue; + break; + } + } + if( array_key_exists('error_code', $resp ) ) + { + $resp['success'] = 0; + } else { + $resp['success'] = 1; + } + $resp['sms'] = $sms[$wrapper_id]; + array_push($response, $resp); + break; + case 'errno': + $err_no = $doc_child->nodeValue; + break; + case 'errdesc': + $err_desc = $doc_child->nodeValue; + break; + } + } + + if (isset($err_no)) { + throw new KannelException($err_desc, $err_no); + } + + if ($single_message) { + return $response[0]; + } else { + return $response; + } + } + + /** + * Check how many SMS credits you have available + * + * @return integer SMS credits remaining + * @deprecated Use checkBalance() instead + * @author Martin Steel + */ + public function checkCredit() { + // Create XML doc for request + $req_doc = new \DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Credit'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Kannel + $resp_xml = $this->postToKannel(self::API_CREDIT_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new \DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to find credit value + $credit; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes AS $doc_child) { + switch ($doc_child->nodeName) { + case "Credit": + $credit = $doc_child->nodeValue; + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new KannelException($err_desc, $err_no); + } + return $credit; + } + + /** + * Check your account balance + * + * @return array Array of account balance: + * @author Martin Steel + */ + public function checkBalance() { + // Create XML doc for request + $req_doc = new \DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Balance'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Kannel + $resp_xml = $this->postToKannel(self::API_BALANCE_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new \DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to find balance value + $balance = null; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes as $doc_child) { + switch ($doc_child->nodeName) { + case "Balance": + $balance = number_format(floatval($doc_child->nodeValue), 2); + break; + case "Currency": + foreach ($doc_child->childNodes as $resp_node) { + switch ($resp_node->tagName) { + case "Symbol": + $symbol = $resp_node->nodeValue; + break; + case "Code": + $code = $resp_node->nodeValue; + break; + } + } + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new KannelException($err_desc, $err_no); + } + + return array( 'symbol' => $symbol, 'balance' => $balance, 'code' => $code ); + } + + /** + * Check whether the API Key is valid + * + * @return bool True indicates a valid key + * @author Martin Steel + */ + public function checkKey() { + // Create XML doc for request + $req_doc = new \DOMDocument('1.0', 'UTF-8'); + $root = $req_doc->createElement('Authenticate'); + $req_doc->appendChild($root); + $root->appendChild($req_doc->createElement('Key', $this->key)); + $req_xml = $req_doc->saveXML(); + + // POST XML to Kannel + $resp_xml = $this->postToKannel(self::API_AUTH_METHOD, $req_xml); + + // Create XML doc for response + $resp_doc = new \DOMDocument(); + $resp_doc->loadXML($resp_xml); + + // Parse the response to see if authenticated + $cust_id; + $err_no = null; + $err_desc = null; + + foreach ($resp_doc->documentElement->childNodes AS $doc_child) { + switch ($doc_child->nodeName) { + case "CustID": + $cust_id = $doc_child->nodeValue; + break; + case "ErrNo": + $err_no = $doc_child->nodeValue; + break; + case "ErrDesc": + $err_desc = $doc_child->nodeValue; + break; + default: + break; + } + } + + if (isset($err_no)) { + throw new KannelException($err_desc, $err_no); + } + return isset($cust_id); + } + + /** + * Make an HTTP POST to Kannel + * + * @param string method Kannel method to call (sms/credit) + * @param string data Content of HTTP POST + * + * @return string Response from Kannel + * @author Martin Steel + */ + protected function postToKannel($method, $data) { + if ($this->log) { + $this->logXML("API $method Request XML", $data); + } + + if( isset( $this->ssl ) ) { + $ssl = $this->ssl; + } else { + $ssl = $this->sslSupport(); + } + + $url = $ssl ? 'https://' : 'http://'; + $url .= self::API_BASE_URL . $method; + + $response = $this->xmlPost($url, $data); + + if ($this->log) { + $this->logXML("API $method Response XML", $response); + } + + return $response; + } + + /** + * Make a HTTP POST + * + * cURL will be used if available, otherwise tries the PHP stream functions + * + * @param string url URL to send to + * @param string data Data to POST + * @return string Response returned by server + * @author Martin Steel + */ + protected function xmlPost($url, $data) { + if(extension_loaded('curl')) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml")); + curl_setopt($ch, CURLOPT_USERAGENT, 'Kannel PHP Wrapper/1.0' . self::VERSION); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + if (isset($this->proxy_host) && isset($this->proxy_port)) { + curl_setopt($ch, CURLOPT_PROXY, $this->proxy_host); + curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port); + } + + $response = curl_exec($ch); + $info = curl_getinfo($ch); + + if ($response === false || $info['http_code'] != 200) { + throw new \Exception('HTTP Error calling Kannel API - HTTP Status: ' . $info['http_code'] . ' - cURL Erorr: ' . curl_error($ch)); + } elseif (curl_errno($ch) > 0) { + throw new \Exception('HTTP Error calling Kannel API - cURL Error: ' . curl_error($ch)); + } + + curl_close($ch); + + return $response; + } elseif (function_exists('stream_get_contents')) { + // Enable error Track Errors + $track = ini_get('track_errors'); + ini_set('track_errors',true); + + $params = array('http' => array( + 'method' => 'POST', + 'header' => "Content-Type: text/xml\r\nUser-Agent: mediaburst PHP Wrapper/" . self::VERSION . "\r\n", + 'content' => $data + )); + + if (isset($this->proxy_host) && isset($this->proxy_port)) { + $params['http']['proxy'] = 'tcp://'.$this->proxy_host . ':' . $this->proxy_port; + $params['http']['request_fulluri'] = True; + } + + $ctx = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $ctx); + if (!$fp) { + ini_set('track_errors',$track); + throw new \Exception("HTTP Error calling Kannel API - fopen Error: $php_errormsg"); + } + $response = @stream_get_contents($fp); + if ($response === false) { + ini_set('track_errors',$track); + throw new \Exception("HTTP Error calling Kannel API - stream Error: $php_errormsg"); + } + ini_set('track_errors',$track); + return $response; + } else { + throw new \Exception("Kannel requires PHP5 with cURL or HTTP stream support"); + } + } + + /** + * Does the server/HTTP wrapper support SSL + * + * This is a best guess effort, some servers have weird setups where even + * though cURL is compiled with SSL support is still fails to make + * any requests. + * + * @return bool True if SSL is supported + * @author Martin Steel + */ + protected function sslSupport() { + $ssl = false; + // See if PHP is compiled with cURL + if (extension_loaded('curl')) { + $version = curl_version(); + $ssl = ($version['features'] & CURL_VERSION_SSL) ? true : false; + } elseif (extension_loaded('openssl')) { + $ssl = true; + } + return $ssl; + } + + /** + * Log some XML, tidily if possible, in the PHP error log + * + * @param string log_msg The log message to prepend to the XML + * @param string xml An XML formatted string + * + * @return void + * @author Martin Steel + */ + protected function logXML($log_msg, $xml) { + // Tidy if possible + if (class_exists('tidy')) { + $tidy = new \tidy; + $config = array( + 'indent' => true, + 'input-xml' => true, + 'output-xml' => true, + 'wrap' => 200 + ); + $tidy->parseString($xml, $config, 'utf8'); + $tidy->cleanRepair(); + $xml = $tidy; + } + // Output + error_log("Kannel $log_msg: $xml"); + } + + /** + * Check if an array is associative + * + * @param array $array Array to check + * @return bool + * @author Martin Steel + */ + protected function is_assoc($array) { + return (bool)count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Check if a number is a valid MSISDN + * + * @param string $val Value to check + * @return bool True if valid MSISDN + * @author James Inman + * @since 1.3.0 + * @todo Take an optional country code and check that the number starts with it + */ + public static function is_valid_msisdn($val) { + return preg_match( '/^[1-9][0-9]{7,12}$/', $val ); + } + +} diff --git a/plugin/kannelsms/vendor/license.txt b/plugin/kannelsms/vendor/license.txt new file mode 100644 index 000000000..041edd0c5 --- /dev/null +++ b/plugin/kannelsms/vendor/license.txt @@ -0,0 +1,14 @@ +Copyright (c) 2011 - 2012, Mediaburst Ltd + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/plugin/kannelsms/vendor/readme.md b/plugin/kannelsms/vendor/readme.md new file mode 100644 index 000000000..1492a43d7 --- /dev/null +++ b/plugin/kannelsms/vendor/readme.md @@ -0,0 +1,253 @@ +# Kannel SMS API Wrapper for PHP + +This wrapper lets you interact with Kannel without the hassle of having to create any XML or make HTTP calls. + +## What's Kannel? + +[Kannel][2] is Mediaburst's SMS API. + +### Prerequisites + +* A [Kannel][2] account + +## Usage + +Require the Kannel library: + +```php +require 'class-Kannel.php'; +``` + +### Sending a message + +```php +$kannel = new Kannel( $API_KEY ); +$message = array( 'to' => '441234567891', 'message' => 'This is a test!' ); +$result = $kannel->send( $message ); +``` + +### Sending multiple messages + +We recommend you use batch sizes of 500 messages or fewer. By limiting the batch size it prevents any timeouts when sending. + +```php +$kannel = new Kannel( $API_KEY ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!' ) +); +$results = $kannel->send( $messages ); +``` + +### Handling the response + +The responses come back as arrays, these contain the unique Kannel message ID, whether the message worked (`success`), and the original SMS so you can update your database. + + Array + ( + [id] => VE_164732148 + [success] => 1 + [sms] => Array + ( + [to] => 441234567891 + [message] => This is a test! + ) + + ) + +If you send multiple SMS messages in a single send, you'll get back an array of results, one per SMS. + +The result will look something like this: + + Array + ( + [0] => Array + ( + [id] => VI_143228951 + [success] => 1 + [sms] => Array + ( + [to] => 441234567891 + [message] => This is a test! + ) + + ) + + [1] => Array + ( + [id] => VI_143228952 + [success] => 1 + [sms] => Array + ( + [to] => 441234567892 + [message] => This is a test 2! + ) + + ) + + ) + +If a message fails, the reason for failure will be set in `error_code` and `error_message`. + +For example, if you send to invalid phone number "abc": + + Array + ( + [error_code] => 10 + [error_message] => Invalid 'To' Parameter + [success] => 0 + [sms] => Array + ( + [to] => abc + [message] => This is a test! + ) + + ) + +### Checking your balance + +Check your available SMS balance: + +```php +$kannel = new Kannel( $API_KEY ); +$kannel->checkBalance(); +``` + +This will return: + + Array + ( + [symbol] => £ + [balance] => 351.91 + [code] => GBP + ) + +### Handling Errors + +The Kannel wrapper will throw a `KannelException` if the entire call failed. + +```php +try +{ + $kannel = new Kannel( 'invalid_key' ); + $message = array( 'to' => 'abc', 'message' => 'This is a test!' ); + $result = $kannel->send( $message ); +} +catch( KannelException $e ) +{ + print $e->getMessage(); + // Invalid API Key +} +``` + +### Advanced Usage + +This class has a few additional features that some users may find useful, if these are not set your account defaults will be used. + +### Optional Parameters + +See the [Kannel Documentation](http://www.kannelsms.com/doc/clever-stuff/xml-interface/send-sms/) for full details on these options. + +* $from [string] + + The from address displayed on a phone when they receive a message + +* $long [boolean] + + Enable long SMS. A standard text can contain 160 characters, a long SMS supports up to 459. + +* $truncate [nullable boolean] + + Truncate the message payload if it is too long, if this is set to false, the message will fail if it is too long. + +* $invalid_char_action [string] + + What to do if the message contains an invalid character. Possible values are + * error - Fail the message + * remove - Remove the invalid characters then send + * replace - Replace some common invalid characters such as replacing curved quotes with straight quotes + +* $ssl [boolean, default: true] + + Use SSL when making an HTTP request to the Kannel API + + +### Setting Options + +#### Global Options + +Options set on the API object will apply to all SMS messages unless specifically overridden. + +In this example both messages will be sent from Kannel: + +```php +$options = array( 'from' => 'Kannel' ); +$kannel = new Kannel( $API_KEY, $options ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!' ) +); +$results = $kannel->send( $messages ); +``` + +#### Per-message Options + +Set option values individually on each message. + +In this example, one message will be from Kannel and the other from 84433: + +```php +$kannel = new Kannel( $API_KEY, $options ); +$messages = array( + array( 'to' => '441234567891', 'message' => 'This is a test!', 'from' => 'Kannel' ), + array( 'to' => '441234567892', 'message' => 'This is a test 2!', 'from' => '84433' ) +); +$results = $kannel->send( $messages ); +``` + +### SSL Errors + +Due to the huge variety of PHP setups out there a small proportion of users may get PHP errors when making API calls due to their SSL configuration. + +The errors will generally look something like this: + +``` +Fatal error: +Uncaught exception 'Exception' with message 'HTTP Error calling Kannel API +HTTP Status: 0 +cURL Erorr: SSL certificate problem, verify that the CA cert is OK. +Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed' +``` + +If you're seeing this error there are two fixes available, the first is easy, simply disable SSL on Kannel calls. Alternatively you can setup your PHP install with the correct root certificates. + +#### Disable SSL on Kannel calls + +```php +$options = array( 'ssl' => false ); +$kannel = new Kannel( $API_KEY, $options ); +``` + +#### Setup SSL root certificates on your server + +This is much more complicated as it depends on your setup, however there are many guides available online. +Try a search term like "windows php curl root certificates" or "ubuntu update root certificates". + + +# License + +This project is licensed under the ISC open-source license. + +A copy of this license can be found in license.txt. + +# Contributing + +If you have any feedback on this wrapper drop us an email to [hello@kannelsms.com][1]. + +The project is hosted on GitHub at [https://github.com/mediaburst/kannel-php][3]. +If you would like to contribute a bug fix or improvement please fork the project +and submit a pull request. + +[1]: mailto:hello@kannelsms.com +[2]: http://www.kannelsms.com/ +[3]: https://github.com/mediaburst/kannel-php diff --git a/plugin/keycloak/KeycloakPlugin.php b/plugin/keycloak/KeycloakPlugin.php new file mode 100644 index 000000000..c876ca249 --- /dev/null +++ b/plugin/keycloak/KeycloakPlugin.php @@ -0,0 +1,62 @@ + 'boolean', + ] + ); + } + + /** + * @return $this + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @return string + */ + public function get_block_title() + { + return $this->get('block_title'); + } + + /** + * @return string + */ + public function get_content() + { + return $this->get('content'); + } + + /** + * Deletes all keycloak chamilo session data. + */ + public function logout() + { + Session::erase('samlUserdata'); + Session::erase('samlNameId'); + Session::erase('samlNameIdFormat'); + Session::erase('samlSessionIndex'); + Session::erase('AuthNRequestID'); + } +} diff --git a/plugin/keycloak/README.md b/plugin/keycloak/README.md new file mode 100644 index 000000000..7d0b079a7 --- /dev/null +++ b/plugin/keycloak/README.md @@ -0,0 +1,54 @@ +Keycloak +============== + +1. Enable the plugin. +2. Create a new settings.php file here plugin/keycloak/settings.php you can find an example here: +plugin/keycloak/settings.dist.php + +3. Edit the settings.php file with your Keycloak settings: + +
    +'idp' => array(
    +    'entityId' => 'http://localhost:8080/auth/realms/master',
    +    'singleSignOnService' => array (
    +        'url' => 'http://localhost:8080/auth/realms/master/protocol/saml',
    +    ),
    +    'singleLogoutService' => array (
    +        'url' => 'http://localhost:8080/auth/realms/master/protocol/saml',
    +    ),
    +    'x509cert' => 'xxx',
    +)
    +
    + +4. Configure your keycloak server with the following settings: + +* Create a client with "client id" value: + http://www.example.org/plugin/keycloak/metadata.php + +* Valid redirect URIs: * +* Client Protocol: saml +* Logout service redirect Binding URL: http://www.example.org/plugin/keycloak/start.php?sls + + +Change the client scope roles to "Single role attribute". + +- Client Scopes-> role_list -> Mappers -> role list -> "Single Role Attribute" = true + +Add user mappers for "FirstName", "LastName" and "Email" so Chamilo can get those values. + +Clients -> (select the client previously created) -> mappers -> create + +Name: Email +Mapper Type = User Property. +User Attribute: Email +Friendly Name: Email +SAML Attribute Name: Email +SAML Attribute: Basic + +Repeat the process for the 3 attributes. + +Create a demo user in keycloak + +Try to login using the new keycloak button in Chamilo. + +When everything works, restrict `Valid redirect URIs` setting from `*` to `http://www.example.org/plugin/keycloak/start.php?acs` diff --git a/plugin/keycloak/index.php b/plugin/keycloak/index.php new file mode 100644 index 000000000..19759d0ca --- /dev/null +++ b/plugin/keycloak/index.php @@ -0,0 +1,27 @@ +get_content(); +$title = $plugin->get_block_title(); +$title = $title ? "

    $title

    " : ''; + +$css = $plugin->get_css(); +$css = $css ? "" : ''; + +if (empty($content)) { + echo ''; +} + +echo << + $css + +

    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 r$1nER-&uWq(EMo_`#14*`~mY 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 literal 0 HcmV?d00001 diff --git a/plugin/jcapture/lib/httpclient-4.0.1.jar b/plugin/jcapture/lib/httpclient-4.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..d1433c799519676696c1bddaa120f00038e60154 GIT binary patch literal 320260 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

    +EOT; diff --git a/plugin/keycloak/lang/english.php b/plugin/keycloak/lang/english.php new file mode 100644 index 000000000..454301348 --- /dev/null +++ b/plugin/keycloak/lang/english.php @@ -0,0 +1,6 @@ +getSPMetadata(); + $errors = $settings->validateMetadata($metadata); + if (empty($errors)) { + header('Content-Type: text/xml'); + echo $metadata; + } else { + throw new OneLogin\Saml2\Error('Invalid SP metadata: '.implode(', ', $errors), OneLogin\Saml2\Error::METADATA_SP_INVALID); + } +} catch (Exception $e) { + echo $e->getMessage(); +} diff --git a/plugin/keycloak/plugin.php b/plugin/keycloak/plugin.php new file mode 100644 index 000000000..cd8242db5 --- /dev/null +++ b/plugin/keycloak/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/keycloak/settings.dist.php b/plugin/keycloak/settings.dist.php new file mode 100644 index 000000000..3ea8a291b --- /dev/null +++ b/plugin/keycloak/settings.dist.php @@ -0,0 +1,155 @@ + false, + 'debug' => true, + 'sp' => [ + 'entityId' => $spBaseUrl.'metadata.php', + 'assertionConsumerService' => [ + 'url' => $spBaseUrl.'start.php?acs', + ], + 'singleLogoutService' => [ + 'url' => $spBaseUrl.'start.php?sls', + ], + 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', + ], + 'idp' => [ + 'entityId' => $url.'auth/realms/'.$realm, // Example http://localhost:8080/auth/realms/master + 'singleSignOnService' => [ + 'url' => $url.'auth/realms/'.$realm.'/protocol/saml', // example http://localhost:8080/auth/realms/master/protocol/saml + ], + 'singleLogoutService' => [ + 'url' => $url.'auth/realms/'.$realm.'/protocol/saml', // example http://localhost:8080/auth/realms/master/protocol/saml + ], + 'x509cert' => $certificate, + ], +]; + +// advanced settings +// +// +//// Compression settings +//'compress' => array ( +// 'requests' => true, +// 'responses' => true +//), +// // Security settings +// 'security' => array ( +// +// /** signatures and encryptions offered */ +// +// // Indicates that the nameID of the sent by this SP +// // will be encrypted. +// 'nameIdEncrypted' => false, +// +// // Indicates whether the messages sent by this SP +// // will be signed. [Metadata of the SP will offer this info] +// 'authnRequestsSigned' => false, +// +// // Indicates whether the messages sent by this SP +// // will be signed. +// 'logoutRequestSigned' => false, +// +// // Indicates whether the messages sent by this SP +// // will be signed. +// 'logoutResponseSigned' => false, +// +// /* Sign the Metadata +// False || True (use sp certs) || array ( +// keyFileName => 'metadata.key', +// certFileName => 'metadata.crt' +// ) +// */ +// 'signMetadata' => false, +// +// /** signatures and encryptions required **/ +// +// // Indicates a requirement for the , +// // and elements received by this SP to be signed. +// 'wantMessagesSigned' => false, +// +// // Indicates a requirement for the elements received by +// // this SP to be encrypted. +// 'wantAssertionsEncrypted' => false, +// +// // Indicates a requirement for the elements received by +// // this SP to be signed. [Metadata of the SP will offer this info] +// 'wantAssertionsSigned' => false, +// +// // Indicates a requirement for the NameID element on the SAMLResponse +// // received by this SP to be present. +// 'wantNameId' => true, +// +// // Indicates a requirement for the NameID received by +// // this SP to be encrypted. +// 'wantNameIdEncrypted' => false, +// +// // Authentication context. +// // Set to false and no AuthContext will be sent in the AuthNRequest. +// // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'. +// // Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'). +// 'requestedAuthnContext' => true, +// +// // Indicates if the SP will validate all received xmls. +// // (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). +// 'wantXMLValidation' => true, +// +// // If true, SAMLResponses with an empty value at its Destination +// // attribute will not be rejected for this fact. +// 'relaxDestinationValidation' => false, +// +// // Algorithm that the toolkit will use on signing process. Options: +// // 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' +// // 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' +// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' +// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' +// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' +// // Notice that sha1 is a deprecated algorithm and should not be used +// 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', +// +// // Algorithm that the toolkit will use on digest process. Options: +// // 'http://www.w3.org/2000/09/xmldsig#sha1' +// // 'http://www.w3.org/2001/04/xmlenc#sha256' +// // 'http://www.w3.org/2001/04/xmldsig-more#sha384' +// // 'http://www.w3.org/2001/04/xmlenc#sha512' +// // Notice that sha1 is a deprecated algorithm and should not be used +// 'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256', +// +// // ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses +// // uppercase. Turn it True for ADFS compatibility on signature verification +// 'lowercaseUrlencoding' => false, +//), +// +// // Contact information template, it is recommended to supply a +// // technical and support contacts. +// 'contactPerson' => array ( +// 'technical' => array ( +// 'givenName' => 'example', +// 'emailAddress' => 'test@example.org' +// ), +// 'support' => array ( +// 'givenName' => 'example', +// 'emailAddress' => 'test@example.org' +// ), +//), +// +// // Organization information template, the info in en_US lang is +// // recomended, add more if required. +// 'organization' => array ( +// 'en-US' => array( +// 'name' => 'chamilo', +// 'displayname' => 'chamilo', +// 'url' => 'chamilo.org' +// ), +//), diff --git a/plugin/keycloak/start.php b/plugin/keycloak/start.php new file mode 100644 index 000000000..0bc7f305a --- /dev/null +++ b/plugin/keycloak/start.php @@ -0,0 +1,220 @@ +getRequest(); +$idpData = $settings->getIdPData(); + +if (isset($_GET['sso'])) { + $auth->login(); +// If AuthNRequest ID need to be saved in order to later validate it, do instead + /*$ssoBuiltUrl = $auth->login(null, [], false, false, true); + $_SESSION['AuthNRequestID'] = $auth->getLastRequestID(); + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); + header('Location: ' . $ssoBuiltUrl); + exit();*/ +} elseif (isset($_GET['slo'])) { + /* + if (isset($idpData['singleLogoutService']) && isset($idpData['singleLogoutService']['url'])) { + $sloUrl = $idpData['singleLogoutService']['url']; + } else { + throw new Exception("The IdP does not support Single Log Out"); + } + + if (isset($_SESSION['samlSessionIndex']) && !empty($_SESSION['samlSessionIndex'])) { + $logoutRequest = new \OneLogin\Saml2\LogoutRequest($settings, null, $_SESSION['samlSessionIndex']); + } else { + $logoutRequest = new \OneLogin\Saml2\LogoutRequest($settings); + } + $samlRequest = $logoutRequest->getRequest(); + $parameters = array('SAMLRequest' => $samlRequest); + $url = \OneLogin\Saml2\Utils::redirect($sloUrl, $parameters, true); + header("Location: $url"); + exit;*/ + $returnTo = null; + $parameters = []; + $nameId = Session::read('samlNameId'); + $sessionIndex = Session::read('samlSessionIndex'); + $nameIdFormat = Session::read('samlNameIdFormat'); + $auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat); + +// If LogoutRequest ID need to be saved in order to later validate it, do instead + // $sloBuiltUrl = $auth->logout(null, [], $nameId, $sessionIndex, true); + /*$_SESSION['LogoutRequestID'] = $auth->getLastRequestID(); + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); + header('Location: ' . $sloBuiltUrl); + exit();*/ +} elseif (isset($_GET['acs'])) { + $requestID = Session::read('AuthNRequestID'); + $auth->processResponse($requestID); + $errors = $auth->getErrors(); + if (!empty($errors)) { + $content .= '

    '.implode(', ', $errors).'

    '; + } + + if (!$auth->isAuthenticated()) { + api_not_allowed(true, $content.'

    Not authenticated

    '); + exit; + } + + $keyCloackUserName = $auth->getNameId(); + $userInfo = api_get_user_info_from_username($keyCloackUserName); + $attributes = $auth->getAttributes(); + $userId = 0; + if (!empty($attributes) && empty($userInfo)) { + $firstName = ''; + if (isset($attributes['FirstName']) && !empty($attributes['FirstName'])) { + $firstName = reset($attributes['FirstName']); + } + + $lastName = ''; + if (isset($attributes['LastName']) && !empty($attributes['LastName'])) { + $lastName = reset($attributes['LastName']); + } + + $email = ''; + if (isset($attributes['Email']) && !empty($attributes['Email'])) { + $email = reset($attributes['Email']); + } + + if (empty($email)) { + api_not_allowed(true); + } + + $userId = UserManager::create_user( + $firstName, + $lastName, + STUDENT, + $email, + $keyCloackUserName, + '', + '', + '', + '', + '', + 'keycloak' + ); + + if ($userId) { + $userInfo = api_get_user_info($userId); + } + } else { + // Only load users that were created using this method. + if ($userInfo['auth_source'] === 'keycloak') { + $userId = $userInfo['user_id']; + } + } + + if (!empty($userId)) { + // Set chamilo sessions + Session::write('samlUserdata', $auth->getAttributes()); + Session::write('samlNameId', $auth->getNameId()); + Session::write('samlNameIdFormat', $auth->getNameIdFormat()); + Session::write('samlSessionIndex', $auth->getSessionIndex()); + Session::erase('AuthNRequestID'); + + // Filling session variables with new data + Session::write('_uid', $userId); + Session::write('_user', $userInfo); + Session::write('is_platformAdmin', false); + Session::write('is_allowedCreateCourse', false); + } else { + Display::addFlash(Display::return_message(get_lang('InvalidId'))); + } + + /*if (isset($_POST['RelayState']) && \OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) { + $auth->redirectTo($_POST['RelayState']); + }*/ + header('Location: '.api_get_path(WEB_PATH)); + exit; +} elseif (isset($_GET['sls'])) { + $requestID = Session::read('LogoutRequestID'); + $auth->processSLO(false, $requestID); + $errors = $auth->getErrors(); + + if (empty($errors)) { + Session::erase('samlNameId'); + Session::erase('samlSessionIndex'); + Session::erase('samlNameIdFormat'); + Session::erase('samlUserdata'); + Session::erase('AuthNRequestID'); + Session::erase('LogoutRequestID'); + + Display::addFlash(Display::return_message('Sucessfully logged out')); + header('Location: '.api_get_path(WEB_PATH)); + exit; + } else { + api_not_allowed(true, implode(', ', $errors)); + } +} + +$template = new Template(''); + +if (isset($_SESSION['samlUserdata'])) { + $attributes = Session::read('samlUserdata'); + $params = []; + if (!empty($attributes)) { + $content .= 'You have the following attributes:
    '; + $content .= ''; + foreach ($attributes as $attributeName => $attributeValues) { + $content .= ''; + } + $content .= '
    NameValues
    '.htmlentities($attributeName).'
      '; + foreach ($attributeValues as $attributeValue) { + $content .= '
    • '.htmlentities($attributeValue).'
    • '; + } + $content .= '
    '; + } else { + $content .= "

    You don't have any attribute

    "; + } + + $content .= '
    '; +} else { + $content .= '

    Login

    '; + $content .= '

    Login and access to attrs.php page

    '; +} + +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/learning_calendar/LearningCalendarPlugin.php b/plugin/learning_calendar/LearningCalendarPlugin.php new file mode 100644 index 000000000..e1397a12d --- /dev/null +++ b/plugin/learning_calendar/LearningCalendarPlugin.php @@ -0,0 +1,1152 @@ + 'boolean']); + $this->setHasPersonalEvents(true); + } + + /** + * Event definition. + * + * @return array + */ + public function getEventTypeList() + { + return [ + self::EVENT_TYPE_TAKEN => ['color' => 'red', 'name' => self::get_lang('EventTypeTaken')], + self::EVENT_TYPE_EXAM => ['color' => 'yellow', 'name' => self::get_lang('EventTypeExam')], + self::EVENT_TYPE_FREE => ['color' => 'green', 'name' => self::get_lang('EventTypeFree')], + ]; + } + + /** + * @return array + */ + public function getEventTypeColorList() + { + $list = $this->getEventTypeList(); + $newList = []; + foreach ($list as $eventId => $event) { + $newList[$eventId] = $event['color']; + } + + return $newList; + } + + /** + * Get the class instance. + * + * @return $this + */ + public static function create() + { + static $result = null; + + return $result ?: $result = new self(); + } + + /** + * Get the plugin directory name. + */ + public function get_name() + { + return 'learning_calendar'; + } + + /** + * Install the plugin. Setup the database. + */ + public function install() + { + $sql = " + CREATE TABLE IF NOT EXISTS learning_calendar( + id int not null AUTO_INCREMENT primary key, + title varchar(255) not null default '', + description longtext default null, + total_hours int not null default 0, + minutes_per_day int not null default 0, + disabled int default 0, + author_id int(11) not null + ) + "; + Database::query($sql); + + $sql = " + CREATE TABLE IF NOT EXISTS learning_calendar_events( + id int not null AUTO_INCREMENT primary key, + name varchar(255) default '', + calendar_id int not null, + start_date date not null, + end_date date not null, + type int not null + ) + "; + Database::query($sql); + + $sql = " + CREATE TABLE IF NOT EXISTS learning_calendar_user( + id int not null AUTO_INCREMENT primary key, + user_id int(11) not null, + calendar_id int not null + ) + "; + Database::query($sql); + + $sql = " + CREATE TABLE IF NOT EXISTS learning_calendar_control_point( + id int not null AUTO_INCREMENT primary key, + user_id int(11) not null, + control_date date not null, + control_value int not null, + created_at datetime not null, + updated_at datetime not null + ) + "; + Database::query($sql); + + $extraField = new ExtraField('lp_item'); + $params = [ + 'display_text' => $this->get_lang('LearningCalendarOneDayMarker'), + 'variable' => 'calendar', + 'visible_to_self' => 1, + 'changeable' => 1, + 'visible_to_others' => 1, + 'field_type' => ExtraField::FIELD_TYPE_CHECKBOX, + ]; + + $extraField->save($params); + + $extraField = new ExtraField('course'); + $params = [ + 'display_text' => $this->get_lang('CourseHoursDuration'), + 'variable' => 'course_hours_duration', + 'visible_to_self' => 1, + 'changeable' => 1, + 'visible_to_others' => 1, + 'field_type' => ExtraField::FIELD_TYPE_TEXT, + ]; + + $extraField->save($params); + + return true; + } + + /** + * Uninstall the plugin. + */ + public function uninstall() + { + $tables = [ + 'learning_calendar', + 'learning_calendar_events', + 'learning_calendar_user', + ]; + + foreach ($tables as $table) { + $sql = "DROP TABLE IF EXISTS $table"; + Database::query($sql); + } + + $extraField = new ExtraField('lp_item'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('calendar'); + + if ($fieldInfo) { + $extraField->delete($fieldInfo['id']); + } + + $extraField = new ExtraField('course'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('course_hours_duration'); + if ($fieldInfo) { + $extraField->delete($fieldInfo['id']); + } + + return true; + } + + /** + * @param int $from + * @param int $numberOfItems + * @param int $column + * @param string $direction + * + * @return array + */ + public function getCalendars( + $from, + $numberOfItems, + $column, + $direction = 'DESC' + ) { + $column = (int) $column; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; + $direction = strtoupper($direction); + + if (!in_array($direction, ['ASC', 'DESC'])) { + $direction = 'DESC'; + } + + if (api_is_platform_admin()) { + $sql = 'SELECT * FROM learning_calendar'; + } else { + $userId = api_get_user_id(); + $sql = "SELECT * FROM learning_calendar WHERE author_id = $userId"; + } + + $sql .= " LIMIT $from, $numberOfItems "; + + $result = Database::query($sql); + $list = []; + $link = api_get_path(WEB_PLUGIN_PATH).'learning_calendar/start.php'; + while ($row = Database::fetch_array($result)) { + $id = $row['id']; + $row['title'] = Display::url( + $row['title'], + api_get_path(WEB_PLUGIN_PATH).'learning_calendar/calendar.php?id='.$id + ); + $actions = Display::url( + Display::return_icon('edit.png', get_lang('Edit')), + $link.'?action=edit&id='.$id + ); + + $actions .= Display::url( + Display::return_icon('copy.png', get_lang('Copy')), + $link.'?action=copy&id='.$id + ); + + $actions .= Display::url( + Display::return_icon('delete.png', get_lang('Delete')), + $link.'?action=delete&id='.$id + ); + $row['actions'] = $actions; + $list[] = $row; + } + + return $list; + } + + /** + * @param array $calendarInfo + * @param int $start + * @param int $end + * @param int $type + * @param bool $getCount + * + * @return array + */ + public function getCalendarsEventsByDate($calendarInfo, $start, $end, $type = 0, $getCount = false) + { + if (empty($calendarInfo)) { + if ($getCount) { + return 0; + } + + return []; + } + + $calendarId = (int) $calendarInfo['id']; + $start = (int) $start; + $end = (int) $end; + + $startCondition = ''; + $endCondition = ''; + $typeCondition = ''; + + if ($start !== 0) { + $start = api_get_utc_datetime($start); + $startCondition = "AND start_date >= '".$start."'"; + } + if ($end !== 0) { + $end = api_get_utc_datetime($end); + $endCondition = "AND (end_date <= '".$end."' OR end_date IS NULL)"; + } + + if (!empty($type)) { + $type = (int) $type; + $typeCondition = " AND type = $type "; + } + + $select = '*'; + if ($getCount) { + $select = 'count(id) count '; + } + + $sql = "SELECT $select FROM learning_calendar_events + WHERE calendar_id = $calendarId $startCondition $endCondition $typeCondition"; + $result = Database::query($sql); + + if ($getCount) { + $row = Database::fetch_array($result, 'ASSOC'); + + return $row['count']; + } + + $list = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + $list[] = $row; + } + + return ['calendar' => $calendarInfo, 'events' => $list]; + } + + /** + * @param array $calendarInfo + * + * @return array + */ + public function getFirstCalendarDate($calendarInfo) + { + if (empty($calendarInfo)) { + return []; + } + + $calendarId = (int) $calendarInfo['id']; + + /*if (!empty($type)) { + $type = (int) $type; + $typeCondition = " AND type = $type "; + }*/ + + $sql = "SELECT start_date FROM learning_calendar_events + WHERE calendar_id = $calendarId ORDER BY start_date LIMIT 1"; + $result = Database::query($sql); + $row = Database::fetch_array($result, 'ASSOC'); + + return $row['start_date']; + } + + /** + * @return int + */ + public function getCalendarCount() + { + if (api_is_platform_admin()) { + $sql = 'select count(*) as count FROM learning_calendar'; + } else { + $userId = api_get_user_id(); + $sql = "select count(*) as count FROM learning_calendar WHERE author_id = $userId"; + } + $result = Database::query($sql); + $result = Database::fetch_array($result); + + return (int) $result['count']; + } + + /** + * @param int $calendarId + * + * @return array + */ + public function getUsersPerCalendar($calendarId) + { + $calendarId = (int) $calendarId; + $sql = "SELECT * FROM learning_calendar_user + WHERE calendar_id = $calendarId"; + $result = Database::query($sql); + $list = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + $userInfo = api_get_user_info($row['user_id']); + $userInfo['exam'] = 'exam'; + $list[] = $userInfo; + } + + return $list; + } + + /** + * @param int $calendarId + * + * @return int + */ + public function getUsersPerCalendarCount($calendarId) + { + $calendarId = (int) $calendarId; + $sql = "SELECT count(id) as count FROM learning_calendar_user + WHERE calendar_id = $calendarId"; + $result = Database::query($sql); + $row = Database::fetch_array($result, 'ASSOC'); + + return (int) $row['count']; + } + + /** + * @param int $id + */ + public function toggleVisibility($id) + { + $extraField = new ExtraField('lp_item'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('calendar'); + if ($fieldInfo) { + $itemInfo = $this->getItemVisibility($id); + if (empty($itemInfo)) { + $extraField = new ExtraFieldValue('lp_item'); + $value = 1; + $params = [ + 'field_id' => $fieldInfo['id'], + 'value' => $value, + 'item_id' => $id, + ]; + $extraField->save($params); + } else { + $newValue = (int) $itemInfo['value'] === 1 ? 0 : 1; + $extraField = new ExtraFieldValue('lp_item'); + $params = [ + 'id' => $itemInfo['id'], + 'value' => $newValue, + ]; + $extraField->update($params); + } + } + } + + /** + * @param int $id + * + * @return array + */ + public function getItemVisibility($id) + { + $extraField = new ExtraFieldValue('lp_item'); + $values = $extraField->get_values_by_handler_and_field_variable($id, 'calendar'); + + if (empty($values)) { + return []; + } + + return $values; + } + + /** + * @param int $calendarId + * + * @return array|mixed + */ + public function getCalendar($calendarId) + { + $calendarId = (int) $calendarId; + $sql = "SELECT * FROM learning_calendar WHERE id = $calendarId"; + $result = Database::query($sql); + $item = Database::fetch_array($result, 'ASSOC'); + + return $item; + } + + /** + * @param int $userId + * + * @return array|mixed + */ + public function getUserCalendar($userId) + { + $userId = (int) $userId; + $sql = "SELECT * FROM learning_calendar_user WHERE user_id = $userId"; + $result = Database::query($sql); + $item = Database::fetch_array($result, 'ASSOC'); + + return $item; + } + + /** + * @param int $userId + * @param int $start + * @param int $end + * @param int $type + * @param bool $getCount + * + * @return array|int + */ + public function getUserEvents($userId, $start, $end, $type = 0, $getCount = false) + { + $calendarRelUser = $this->getUserCalendar($userId); + if (!empty($calendarRelUser)) { + $calendar = $this->getCalendar($calendarRelUser['calendar_id']); + + return $this->getCalendarsEventsByDate($calendar, $start, $end, $type, $getCount); + } + + if ($getCount) { + return 0; + } + + return []; + } + + /** + * @param int $userId + * + * @return mixed|string + */ + public function getUserCalendarToString($userId) + { + $calendar = $this->getUserCalendar($userId); + if ($calendar) { + $calendarInfo = $this->getCalendar($calendar['calendar_id']); + + return $calendarInfo['title']; + } + + return ''; + } + + /** + * @param int $calendarId + * @param int $userId + * + * @return bool + */ + public function addUserToCalendar($calendarId, $userId) + { + $calendar = $this->getUserCalendar($userId); + if (empty($calendar)) { + $params = [ + 'calendar_id' => $calendarId, + 'user_id' => $userId, + ]; + + Database::insert('learning_calendar_user', $params); + + return true; + } + + return false; + } + + /** + * @param int $calendarId + * @param int $userId + * + * @return bool + */ + public function updateUserToCalendar($calendarId, $userId) + { + $calendar = $this->getUserCalendar($userId); + if (!empty($calendar)) { + $params = [ + 'calendar_id' => $calendarId, + 'user_id' => $userId, + ]; + + Database::update('learning_calendar_user', $params, ['id = ?' => $calendar['id']]); + } + + return true; + } + + /** + * @param int $calendarId + * @param int $userId + * + * @return bool + */ + public function deleteAllCalendarFromUser($calendarId, $userId) + { + $calendarId = (int) $calendarId; + $userId = (int) $userId; + $sql = "DELETE FROM learning_calendar_user + WHERE user_id = $userId AND calendar_id = $calendarId"; + Database::query($sql); + + return true; + } + + /*public static function getUserCalendar($calendarId, $userId) + { + $params = [ + 'calendar_id' => $calendarId, + 'user_id' => $calendarId, + ]; + + Database::insert('learning_calendar_user', $params); + + return true; + }*/ + + public function getForm(FormValidator &$form) + { + $form->addText('title', get_lang('Title')); + $form->addText('total_hours', get_lang('TotalHours')); + $form->addText('minutes_per_day', get_lang('MinutesPerDay')); + $form->addHtmlEditor('description', get_lang('Description'), false); + } + + /** + * @param Agenda $agenda + * @param int $start + * @param int $end + * + * @return array + */ + public function getPersonalEvents($agenda, $start, $end) + { + $userId = api_get_user_id(); + $events = $this->getUserEvents($userId, $start, $end); + + if (empty($events)) { + return []; + } + + $calendarInfo = $events['calendar']; + $events = $events['events']; + + $list = []; + $typeList = $this->getEventTypeColorList(); + foreach ($events as $row) { + $event = []; + $event['id'] = 'personal_'.$row['id']; + $event['title'] = $calendarInfo['title']; + $event['className'] = 'personal'; + $color = isset($typeList[$row['type']]) ? $typeList[$row['type']] : $typeList[self::EVENT_TYPE_FREE]; + $event['borderColor'] = $color; + $event['backgroundColor'] = $color; + $event['editable'] = false; + $event['sent_to'] = get_lang('Me'); + $event['type'] = 'personal'; + + if (!empty($row['start_date'])) { + $event['start'] = $agenda->formatEventDate($row['start_date']); + $event['start_date_localtime'] = api_get_local_time($row['start_date']); + } + + if (!empty($row['end_date'])) { + $event['end'] = $agenda->formatEventDate($row['end_date']); + $event['end_date_localtime'] = api_get_local_time($row['end_date']); + } + + $event['description'] = 'plugin'; + $event['allDay'] = 1; + $event['parent_event_id'] = 0; + $event['has_children'] = 0; + $list[] = $event; + } + + return $list; + } + + /** + * @param int $userId + * @param array $coursesAndSessions + * + * @return string + */ + public function getGradebookEvaluationListToString($userId, $coursesAndSessions) + { + $list = $this->getGradebookEvaluationList($userId, $coursesAndSessions); + + $html = ''; + if (!empty($list)) { + $html = implode(' ', array_column($list, 'name')); + } + + return $html; + } + + /** + * @param int $userId + * @param array $coursesAndSessions + * + * @return array + */ + public function getGradebookEvaluationList($userId, $coursesAndSessions) + { + $userId = (int) $userId; + + if (empty($coursesAndSessions)) { + return 0; + } + + $courseSessionConditionToString = ''; + foreach ($coursesAndSessions as $sessionId => $courseList) { + if (isset($courseList['course_list'])) { + $courseList = array_keys($courseList['course_list']); + } + if (empty($courseList)) { + continue; + } + //$courseListToString = implode("','", $courseList); + /*if (empty($sessionId)) { + $courseAndSessionCondition[] = + " c.id IN ('$courseListToString') "; + } else { + $courseAndSessionCondition[] = " + ( + c.id IN ('$courseListToString') + )"; + }*/ + $courseSessionConditionToString = " AND c.id IN ('".implode("','", $courseList)."') "; + } + + if (empty($courseSessionConditionToString)) { + return 0; + } + + $tableEvaluation = Database::get_main_table(TABLE_MAIN_GRADEBOOK_EVALUATION); + $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE); + $tableResult = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT); + $sql = "SELECT DISTINCT e.name, e.id + FROM $tableEvaluation e + INNER JOIN $tableCourse c + ON (course_code = c.code) + INNER JOIN $tableResult r + ON (r.evaluation_id = e.id) + WHERE + e.type = 'evaluation' AND + r.score >= 2 AND + r.user_id = $userId + $courseSessionConditionToString + "; + $result = Database::query($sql); + $list = []; + if (Database::num_rows($result)) { + while ($row = Database::fetch_array($result, 'ASSOC')) { + $list[$row['id']] = $row; + } + } + + return $list; + } + + /** + * @param int $userId + * @param array $coursesAndSessions + * + * @return int + */ + public function getItemCountChecked($userId, $coursesAndSessions) + { + $userId = (int) $userId; + + if (empty($coursesAndSessions)) { + return 0; + } + + $tableItem = Database::get_course_table(TABLE_LP_ITEM); + $tableLp = Database::get_course_table(TABLE_LP_MAIN); + $tableLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW); + $tableLpView = Database::get_course_table(TABLE_LP_VIEW); + $extraField = new ExtraField('lp_item'); + $fieldInfo = $extraField->get_handler_field_info_by_field_variable('calendar'); + + if (empty($fieldInfo)) { + return 0; + } + + $courseAndSessionCondition = []; + foreach ($coursesAndSessions as $sessionId => $courseList) { + if (isset($courseList['course_list'])) { + $courseList = array_keys($courseList['course_list']); + } + if (empty($courseList)) { + continue; + } + $courseListToString = implode("','", $courseList); + if (empty($sessionId)) { + $courseAndSessionCondition[] = + " ((l.session_id = 0 OR l.session_id is NULL) AND i.c_id IN ('$courseListToString'))"; + } else { + $courseAndSessionCondition[] = " + ( + ((l.session_id = 0 OR l.session_id is NULL) OR l.session_id = $sessionId) AND + i.c_id IN ('$courseListToString') + )"; + } + } + + if (empty($courseAndSessionCondition)) { + return 0; + } + + $courseSessionConditionToString = 'AND ('.implode(' OR ', $courseAndSessionCondition).') '; + $sql = "SELECT count(*) as count + FROM $tableItem i INNER JOIN $tableLp l + ON (i.c_id = l.c_id AND i.lp_id = l.iid) + INNER JOIN $tableLpItemView iv + ON (iv.c_id = l.c_id AND i.iid = iv.lp_item_id) + INNER JOIN $tableLpView v + ON (v.c_id = l.c_id AND v.lp_id = l.iid AND iv.lp_view_id = v.iid) + INNER JOIN extra_field_values e + ON (e.item_id = i.iid AND value = 1 AND field_id = ".$fieldInfo['id'].") + WHERE + v.user_id = $userId AND + status = 'completed' + $courseSessionConditionToString + GROUP BY iv.view_count + "; + + $result = Database::query($sql); + + if (Database::num_rows($result)) { + $row = Database::fetch_array($result, 'ASSOC'); + + return $row['count']; + } + + return 0; + } + + /** + * @param array $htmlHeadXtra + */ + public function setJavaScript(&$htmlHeadXtra) + { + $htmlHeadXtra[] = api_get_js('jqplot/jquery.jqplot.js'); + $htmlHeadXtra[] = api_get_js('jqplot/plugins/jqplot.dateAxisRenderer.js'); + $htmlHeadXtra[] = api_get_js('jqplot/plugins/jqplot.canvasOverlay.js'); + $htmlHeadXtra[] = api_get_js('jqplot/plugins/jqplot.pointLabels.js'); + $htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/jqplot/jquery.jqplot.css'); + } + + /** + * @param int $userId + * @param array $courseAndSessionList + * + * @return string + */ + public function getUserStatsPanel($userId, $courseAndSessionList) + { + // @todo use translation + // get events from this year to today + $stats = $this->getUserStats($userId, $courseAndSessionList); + $html = $this->get_lang('NumberDaysAccumulatedInCalendar').$stats['user_event_count']; + if (!empty($courseAndSessionList)) { + $html .= '
    '; + $html .= $this->get_lang('NumberDaysAccumulatedInLp').$stats['completed']; + $html .= '
    '; + $html .= $this->get_lang('NumberDaysInRetard').' '.($stats['completed'] - $stats['user_event_count']); + } + + $controlList = $this->getControlPointsToPlot($userId); + + if (!empty($controlList)) { + $listToString = json_encode($controlList); + $date = $this->get_lang('Date'); + $controlPoint = $this->get_lang('NumberOfDays'); + + $html .= '
    '; + $html .= ''; + } + + $html = Display::panel($html, $this->get_lang('LearningCalendar')); + + return $html; + } + + /** + * @param int $userId + * @param array $courseAndSessionList + * + * @return array + */ + public function getUserStats($userId, $courseAndSessionList) + { + // Get events from this year to today + $takenCount = $this->getUserEvents( + $userId, + strtotime(date('Y-01-01')), + time(), + self::EVENT_TYPE_TAKEN, + true + ); + + $completed = 0; + $diff = 0; + if (!empty($courseAndSessionList)) { + $completed = $this->getItemCountChecked($userId, $courseAndSessionList); + $diff = $takenCount - $completed; + } + + return [ + 'user_event_count' => $takenCount, + 'completed' => $completed, + 'diff' => $diff, + ]; + } + + /** + * @param int $calendarId + * + * @return bool + */ + public function copyCalendar($calendarId) + { + $item = $this->getCalendar($calendarId); + $this->protectCalendar($item); + $item['author_id'] = api_get_user_id(); + + if (empty($item)) { + return false; + } + + $calendarId = (int) $calendarId; + + unset($item['id']); + //$item['title'] = $item['title']; + + $newCalendarId = Database::insert('learning_calendar', $item); + if (!empty($newCalendarId)) { + $sql = "SELECT * FROM learning_calendar_events WHERE calendar_id = $calendarId"; + $result = Database::query($sql); + while ($row = Database::fetch_array($result, 'ASSOC')) { + unset($row['id']); + $row['calendar_id'] = $newCalendarId; + Database::insert('learning_calendar_events', $row); + } + + return true; + } + + return false; + } + + /** + * @param int $calendarId + * + * @return bool + */ + public function deleteCalendar($calendarId) + { + $item = $this->getCalendar($calendarId); + $this->protectCalendar($item); + + if (empty($item)) { + return false; + } + + $calendarId = (int) $calendarId; + + $sql = "DELETE FROM learning_calendar WHERE id = $calendarId"; + Database::query($sql); + + // Delete events + $sql = "DELETE FROM learning_calendar_events WHERE calendar_id = $calendarId"; + Database::query($sql); + + return true; + } + + /** + * @param int $calendarId + * @param string $startDate + */ + public function toogleDayType($calendarId, $startDate) + { + $startDate = Database::escape_string($startDate); + $calendarId = (int) $calendarId; + + $eventTypeList = $this->getEventTypeColorList(); + // Remove the free type to loop correctly when toogle days. + unset($eventTypeList[self::EVENT_TYPE_FREE]); + + $sql = "SELECT * FROM learning_calendar_events + WHERE start_date = '$startDate' AND calendar_id = $calendarId "; + $result = Database::query($sql); + + if (Database::num_rows($result)) { + $row = Database::fetch_array($result, 'ASSOC'); + $currentType = $row['type']; + $currentType++; + if ($currentType > count($eventTypeList)) { + Database::delete( + 'learning_calendar_events', + [' calendar_id = ? AND start_date = ?' => [$calendarId, $startDate]] + ); + } else { + $params = [ + 'type' => $currentType, + ]; + Database::update( + 'learning_calendar_events', + $params, + [' calendar_id = ? AND start_date = ?' => [$calendarId, $startDate]] + ); + } + } else { + $params = [ + 'name' => '', + 'calendar_id' => $calendarId, + 'start_date' => $startDate, + 'end_date' => $startDate, + 'type' => self::EVENT_TYPE_TAKEN, + ]; + Database::insert('learning_calendar_events', $params); + } + } + + /** + * @param int $calendarId + * + * @return array + */ + public function getEvents($calendarId) + { + $calendarId = (int) $calendarId; + $eventTypeList = $this->getEventTypeColorList(); + + $sql = "SELECT * FROM learning_calendar_events + WHERE calendar_id = $calendarId "; + $result = Database::query($sql); + + $list = []; + while ($row = Database::fetch_array($result, 'ASSOC')) { + $list[] = [ + 'start_date' => $row['start_date'], + 'end_date' => $row['start_date'], + 'color' => $eventTypeList[$row['type']], + ]; + } + + return $list; + } + + public function protectCalendar(array $calendarInfo) + { + $allow = api_is_platform_admin() || api_is_teacher(); + + if (!$allow) { + api_not_allowed(true); + } + + if (!empty($calendarInfo)) { + if (!api_is_platform_admin() && api_is_teacher()) { + if ($calendarInfo['author_id'] != api_get_user_id()) { + api_not_allowed(true); + } + } + } + } + + /** + * @param int $userId + * + * @return array + */ + public function getControlPoints($userId) + { + $userId = (int) $userId; + $sql = "SELECT control_date, control_value + FROM learning_calendar_control_point + WHERE user_id = $userId + ORDER BY control_date"; + $result = Database::query($sql); + $list = Database::store_result($result, 'ASSOC'); + + return $list; + } + + /** + * @param int $userId + * + * @return array + */ + public function getControlPointsToPlot($userId) + { + $list = $this->getControlPoints($userId); + $points = []; + foreach ($list as $item) { + $points[] = [$item['control_date'], $item['control_value']]; + } + + return $points; + } + + /** + * @param int $userId + * @param int $value + */ + public function addControlPoint($userId, $value) + { + $userId = (int) $userId; + $value = (int) $value; + $local = api_get_local_time(); + $date = substr($local, 0, 10); + + $sql = "SELECT id + FROM learning_calendar_control_point + WHERE user_id = $userId AND control_date = '$date'"; + $result = Database::query($sql); + + if (Database::num_rows($result)) { + $params = [ + 'control_value' => $value, + 'updated_at' => api_get_utc_datetime(), + ]; + $data = Database::fetch_array($result); + $id = $data['id']; + Database::update('learning_calendar_control_point', $params, ['id = ?' => $id]); + } else { + $params = [ + 'user_id' => $userId, + 'control_date' => $date, + 'control_value' => $value, + 'created_at' => api_get_utc_datetime(), + 'updated_at' => api_get_utc_datetime(), + ]; + Database::insert('learning_calendar_control_point', $params); + } + } + + public function getAddUserToCalendarForm(FormValidator &$form) + { + $calendars = $this->getCalendars(0, 1000, ''); + + if (empty($calendars)) { + echo Display::return_message(get_lang('NoData'), 'warning'); + exit; + } + $calendars = array_column($calendars, 'title', 'id'); + $calendars = array_map('strip_tags', $calendars); + + $form->addSelect('calendar_id', get_lang('Calendar'), $calendars, ['disable_js' => true]); + } +} diff --git a/plugin/learning_calendar/README.md b/plugin/learning_calendar/README.md new file mode 100644 index 000000000..21236ccbc --- /dev/null +++ b/plugin/learning_calendar/README.md @@ -0,0 +1,2 @@ +## Learning calendar + diff --git a/plugin/learning_calendar/ajax.php b/plugin/learning_calendar/ajax.php new file mode 100644 index 000000000..33aad3bfc --- /dev/null +++ b/plugin/learning_calendar/ajax.php @@ -0,0 +1,43 @@ +getCalendar($calendarId); +$plugin->protectCalendar($item); + +switch ($action) { + case 'toggle_day': + $startDate = isset($_REQUEST['start_date']) ? $_REQUEST['start_date'] : ''; + if (empty($startDate)) { + exit; + } + $endDate = isset($_REQUEST['end_date']) ? $_REQUEST['end_date'] : ''; + if ($startDate == $endDate) { + // One day + $plugin->toogleDayType($calendarId, $startDate); + } else { + // A list of days + $startDateTime = new DateTime($startDate); + $endDateTime = new DateTime($endDate); + $diff = $startDateTime->diff($endDateTime); + $countDays = $diff->format('%a'); + $dayList[] = $startDate; + for ($i = 0; $i < $countDays; $i++) { + $startDateTime->modify('+1 day'); + $dayList[] = $startDateTime->format('Y-m-d'); + } + foreach ($dayList as $day) { + $plugin->toogleDayType($calendarId, $day); + } + } + break; + case 'get_events': + $list = $plugin->getEvents($calendarId); + echo json_encode($list); + break; +} diff --git a/plugin/learning_calendar/calendar.php b/plugin/learning_calendar/calendar.php new file mode 100644 index 000000000..45df9a76e --- /dev/null +++ b/plugin/learning_calendar/calendar.php @@ -0,0 +1,50 @@ +getCalendar($calendarId); +$plugin->protectCalendar($item); + +$isoCode = api_get_language_isocode(); +$htmlHeadXtra[] = api_get_asset('bootstrap-year-calendar/js/bootstrap-year-calendar.js'); +$calendarLanguage = 'en'; +if ($isoCode !== 'en') { + $file = 'bootstrap-year-calendar/js/languages/bootstrap-year-calendar.'.$isoCode.'.js'; + $path = api_get_path(SYS_PUBLIC_PATH).'assets/'.$file; + if (file_exists($path)) { + $htmlHeadXtra[] = api_get_asset($file); + $calendarLanguage = $isoCode; + } +} + +$htmlHeadXtra[] = api_get_css_asset('bootstrap-year-calendar/css/bootstrap-year-calendar.css'); + +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; +$formToString = ''; + +$template = new Template(); +$actionLeft = Display::url( + Display::return_icon( + 'back.png', + get_lang('Add'), + null, + ICON_SIZE_MEDIUM + ), + api_get_path(WEB_PLUGIN_PATH).'learning_calendar/start.php' +); + +$actions = Display::toolbarAction('toolbar-forum', [$actionLeft]); + +$eventList = $plugin->getEventTypeList(); +$template->assign('events', $eventList); +$template->assign('calendar_language', $calendarLanguage); +$template->assign('ajax_url', api_get_path(WEB_PLUGIN_PATH).'learning_calendar/ajax.php?id='.$calendarId); +$template->assign('header', $item['title']); +$content = $template->fetch('learning_calendar/view/calendar.tpl'); +$template->assign('actions', $actions); +$template->assign('content', $content); + +$template->display_one_col_template(); diff --git a/plugin/learning_calendar/calendar_users.php b/plugin/learning_calendar/calendar_users.php new file mode 100644 index 000000000..3a54de800 --- /dev/null +++ b/plugin/learning_calendar/calendar_users.php @@ -0,0 +1,108 @@ +protectCalendar($calendarId); +$item = $plugin->getCalendar($calendarId); + +if (empty($item)) { + api_not_allowed(true); +} + +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; +$formToString = ''; +$template = new Template(); +$actionLeft = Display::url( + Display::return_icon( + 'back.png', + get_lang('Add'), + null, + ICON_SIZE_MEDIUM + ), + api_get_path(WEB_PLUGIN_PATH).'learning_calendar/start.php' +); + +$actions = Display::toolbarAction('toolbar-forum', [$actionLeft]); + +// jqgrid will use this URL to do the selects +$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_calendar_users&id='.$calendarId; + +// The order is important you need to check the the $column variable in the model.ajax.php file +$columns = [ + get_lang('FirstName'), + get_lang('LastName'), + get_lang('Exam'), +]; + +// Column config +$column_model = [ + ['name' => 'firstname', 'index' => 'firstname', 'width' => '35', 'align' => 'left', 'sortable' => 'false'], + ['name' => 'lastname', 'index' => 'lastname', 'width' => '35', 'align' => 'left', 'sortable' => 'false'], + [ + 'name' => 'exam', + 'index' => 'exam', + 'width' => '20', + 'align' => 'center', + 'sortable' => 'false', + ], +]; + +// Autowidth +$extraParams['autowidth'] = 'true'; +// height auto +$extraParams['height'] = 'auto'; +$extraParams['sortname'] = 'name'; +$extraParams['sortorder'] = 'desc'; +$extraParams['multiselect'] = true; + +$deleteIcon = Display::return_icon('delete.png', get_lang('Delete'), null, ICON_SIZE_SMALL); +$urlStats = api_get_path(WEB_CODE_PATH); +$action_links = ''; +$deleteUrl = ''; + +// Add the JS needed to use the jqgrid +$htmlHeadXtra[] = api_get_jqgrid_js(); + +Display::display_header(); + +?> + +'; +echo ''. + Display::return_icon('back.png', get_lang('BackTo').' '.get_lang('PlatformAdmin'), '', '32'). + ''; +echo '

    Logout

    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 FB&k}#7T`c+om#^&8&D?*dU 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

  • '; +echo Display::grid_html('usergroups'); + +Display::display_footer(); diff --git a/plugin/learning_calendar/install.php b/plugin/learning_calendar/install.php new file mode 100644 index 000000000..e6039a4fe --- /dev/null +++ b/plugin/learning_calendar/install.php @@ -0,0 +1,8 @@ +install(); diff --git a/plugin/learning_calendar/lang/english.php b/plugin/learning_calendar/lang/english.php new file mode 100644 index 000000000..78cf5b909 --- /dev/null +++ b/plugin/learning_calendar/lang/english.php @@ -0,0 +1,22 @@ +get_info(); diff --git a/plugin/learning_calendar/start.php b/plugin/learning_calendar/start.php new file mode 100644 index 000000000..a1dbe739c --- /dev/null +++ b/plugin/learning_calendar/start.php @@ -0,0 +1,191 @@ +getForm($form); + $form->addButtonSave(get_lang('Save')); + $formToString = $form->returnForm(); + + if ($form->validate()) { + $values = $form->getSubmitValues(); + $params = [ + 'title' => $values['title'], + 'total_hours' => $values['total_hours'], + 'minutes_per_day' => $values['minutes_per_day'], + 'description' => $values['description'], + 'author_id' => api_get_user_id(), + ]; + Database::insert('learning_calendar', $params); + Display::addFlash(Display::return_message(get_lang('Saved'))); + header('Location: start.php'); + exit; + } + break; + case 'edit': + $form = new FormValidator('calendar', 'post', api_get_self().'?action=edit&id='.$calendarId); + $plugin->getForm($form); + $form->addButtonSave(get_lang('Update')); + $item = $plugin->getCalendar($calendarId); + $plugin->protectCalendar($item); + + if (empty($item)) { + api_not_allowed(true); + } + + $form->setDefaults($item); + $formToString = $form->returnForm(); + + if ($form->validate()) { + $values = $form->getSubmitValues(); + $params = [ + 'title' => $values['title'], + 'total_hours' => $values['total_hours'], + 'minutes_per_day' => $values['minutes_per_day'], + 'description' => $values['description'], + ]; + Database::update('learning_calendar', $params, ['id = ?' => $calendarId]); + Display::addFlash(Display::return_message(get_lang('Updated'))); + header('Location: start.php'); + exit; + } + break; + case 'copy': + $result = $plugin->copyCalendar($calendarId); + if ($result) { + Display::addFlash(Display::return_message(get_lang('Saved'))); + } + header('Location: start.php'); + exit; + + break; + case 'delete': + $result = $plugin->deleteCalendar($calendarId); + if ($result) { + Display::addFlash(Display::return_message(get_lang('Deleted'))); + } + header('Location: start.php'); + exit; + break; + case 'toggle_visibility': + $itemId = isset($_REQUEST['lp_item_id']) ? $_REQUEST['lp_item_id'] : 0; + $lpId = isset($_REQUEST['lp_id']) ? $_REQUEST['lp_id'] : 0; + $plugin->toggleVisibility($itemId); + Display::addFlash(Display::return_message(get_lang('Updated'))); + $url = api_get_path(WEB_CODE_PATH). + 'lp/lp_controller.php?action=add_item&type=step&lp_id='.$lpId.'&'.api_get_cidreq(); + header("Location: $url"); + exit; + break; +} + +$htmlHeadXtra[] = api_get_jqgrid_js(); + +$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_learning_path_calendars'; +$columns = [ + get_lang('Title'), + get_lang('TotalHours'), + get_lang('MinutesPerDay'), + get_lang('Actions'), +]; + +$columnModel = [ + [ + 'name' => 'title', + 'index' => 'title', + 'width' => '300', + 'align' => 'left', + 'sortable' => 'false', + ], + [ + 'name' => 'total_hours', + 'index' => 'total_hours', + 'width' => '100', + 'align' => 'left', + 'sortable' => 'false', + ], + [ + 'name' => 'minutes_per_day', + 'index' => 'minutes_per_day', + 'width' => '100', + 'align' => 'left', + 'sortable' => 'false', + ], + [ + 'name' => 'actions', + 'index' => 'actions', + 'width' => '150', + 'align' => 'left', + //'formatter' => 'action_formatter', + 'sortable' => 'false', + ], +]; + +$extraParams = []; +$extraParams['autowidth'] = 'true'; +// height auto +$extraParams['height'] = 'auto'; + +$template = new Template(); + +if (in_array($action, ['add', 'edit'])) { + $actionLeft = Display::url( + Display::return_icon( + 'back.png', + get_lang('Back'), + null, + ICON_SIZE_MEDIUM + ), + api_get_self().'?'.api_get_cidreq() + ); +} else { + $actionLeft = Display::url( + Display::return_icon( + 'add.png', + get_lang('Add'), + null, + ICON_SIZE_MEDIUM + ), + api_get_self().'?'.api_get_cidreq().'&action=add' + ); + + $content = ''; + + $content .= Display::grid_html('calendars'); + $template->assign('grid', $content); +} + +$template->assign('form', $formToString); +$actions = Display::toolbarAction('toolbar-calendar', [$actionLeft]); +$content = $template->fetch('learning_calendar/view/start.tpl'); +$template->assign('content', $content); +$template->assign('actions', $actions); + +$template->display_one_col_template(); diff --git a/plugin/learning_calendar/uninstall.php b/plugin/learning_calendar/uninstall.php new file mode 100644 index 000000000..5e90e870f --- /dev/null +++ b/plugin/learning_calendar/uninstall.php @@ -0,0 +1,4 @@ +uninstall(); diff --git a/plugin/learning_calendar/view/calendar.tpl b/plugin/learning_calendar/view/calendar.tpl new file mode 100644 index 000000000..32050a169 --- /dev/null +++ b/plugin/learning_calendar/view/calendar.tpl @@ -0,0 +1,104 @@ + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + {% for event in events %} + + + + + {% endfor %} +
    + {{ event.name }}: + + +
    +
    +
    diff --git a/plugin/learning_calendar/view/start.tpl b/plugin/learning_calendar/view/start.tpl new file mode 100644 index 000000000..7bc19acca --- /dev/null +++ b/plugin/learning_calendar/view/start.tpl @@ -0,0 +1,3 @@ +{{ form }} + +{{ grid }} \ No newline at end of file diff --git a/plugin/lti_provider/Entity/Platform.php b/plugin/lti_provider/Entity/Platform.php new file mode 100644 index 000000000..e1fc56d24 --- /dev/null +++ b/plugin/lti_provider/Entity/Platform.php @@ -0,0 +1,234 @@ +id; + } + + /** + * Set id. + */ + public function setId(int $id): Platform + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getToolProvider() + { + return $this->toolProvider; + } + + /** + * @param string $toolProvider + */ + public function setToolProvider(?string $toolProvider): void + { + $this->toolProvider = $toolProvider; + } + + /** + * Get key id. + */ + public function getKid() + { + return $this->kid; + } + + /** + * Set key id. + */ + public function setKid(string $kid): Platform + { + $this->kid = $kid; + + return $this; + } + + /** + * Get Issuer. + */ + public function getIssuer() + { + return $this->issuer; + } + + /** + * Set issuer. + */ + public function setIssuer(string $issuer): Platform + { + $this->issuer = $issuer; + + return $this; + } + + /** + * Get client ID. + */ + public function getClientId() + { + return $this->clientId; + } + + /** + * Set client ID. + */ + public function setClientId(string $clientId): Platform + { + $this->clientId = $clientId; + + return $this; + } + + /** + * Get auth login URL. + */ + public function getAuthLoginUrl() + { + return $this->authLoginUrl; + } + + /** + * Set auth login URL. + */ + public function setAuthLoginUrl(string $authLoginUrl): Platform + { + $this->authLoginUrl = $authLoginUrl; + + return $this; + } + + /** + * Get auth token URL. + */ + public function getAuthTokenUrl() + { + return $this->authTokenUrl; + } + + /** + * Set auth token URL. + */ + public function setAuthTokenUrl(string $authTokenUrl): Platform + { + $this->authTokenUrl = $authTokenUrl; + + return $this; + } + + /** + * Get key set URL. + */ + public function getKeySetUrl() + { + return $this->keySetUrl; + } + + /** + * Set key set URL. + */ + public function setKeySetUrl(string $keySetUrl): Platform + { + $this->keySetUrl = $keySetUrl; + + return $this; + } + + /** + * Get Deployment ID. + */ + public function getDeploymentId() + { + return $this->deploymentId; + } + + /** + * Set Deployment ID. + */ + public function setDeploymentId(string $deploymentId): Platform + { + $this->deploymentId = $deploymentId; + + return $this; + } +} diff --git a/plugin/lti_provider/Entity/PlatformKey.php b/plugin/lti_provider/Entity/PlatformKey.php new file mode 100644 index 000000000..eb76dace1 --- /dev/null +++ b/plugin/lti_provider/Entity/PlatformKey.php @@ -0,0 +1,116 @@ +id; + } + + /** + * Set id. + */ + public function setId(int $id): PlatformKey + { + $this->id = $id; + + return $this; + } + + /** + * Get key id. + */ + public function getKid(): string + { + return $this->kid; + } + + /** + * Set key id. + */ + public function setKid(string $kid): PlatformKey + { + $this->kid = $kid; + + return $this; + } + + /** + * Get privateKey. + */ + public function getPrivateKey(): string + { + return $this->privateKey; + } + + /** + * Set privateKey. + */ + public function setPrivateKey(string $privateKey): PlatformKey + { + $this->privateKey = $privateKey; + + return $this; + } + + /** + * Get publicKey. + */ + public function getPublicKey(): string + { + return $this->publicKey; + } + + /** + * Set publicKey. + */ + public function setPublicKey(string $publicKey): PlatformKey + { + $this->publicKey = $publicKey; + + return $this; + } +} diff --git a/plugin/lti_provider/Entity/Result.php b/plugin/lti_provider/Entity/Result.php new file mode 100644 index 000000000..eecaefc11 --- /dev/null +++ b/plugin/lti_provider/Entity/Result.php @@ -0,0 +1,267 @@ +id; + } + + public function setId(int $id): Result + { + $this->id = $id; + + return $this; + } + + public function getIssuer(): string + { + return $this->issuer; + } + + public function setIssuer(string $issuer): Result + { + $this->issuer = $issuer; + + return $this; + } + + public function getUserId(): int + { + return $this->userId; + } + + public function setUserId(int $userId): Result + { + $this->userId = $userId; + + return $this; + } + + public function getClientUId(): string + { + return $this->clientUId; + } + + public function setClientUId(string $clientUId): Result + { + $this->clientUId = $clientUId; + + return $this; + } + + public function getCourseCode(): string + { + return $this->courseCode; + } + + /** + * @param string $tool + */ + public function setCourseCode(string $courseCode): Result + { + $this->courseCode = $courseCode; + + return $this; + } + + public function getToolId(): int + { + return $this->toolId; + } + + public function setToolId(int $toolId): Result + { + $this->toolId = $toolId; + + return $this; + } + + public function getToolName(): string + { + return $this->toolName; + } + + public function setToolName(string $toolName): Result + { + $this->toolName = $toolName; + + return $this; + } + + public function getScore(): float + { + return $this->score; + } + + public function setScore(float $score): Result + { + $this->score = $score; + + return $this; + } + + public function getProgress(): int + { + return $this->progress; + } + + public function setProgress(int $progress): Result + { + $this->progress = $progress; + + return $this; + } + + public function getDuration(): int + { + return $this->duration; + } + + public function setDuration(int $duration): Result + { + $this->duration = $duration; + + return $this; + } + + public function getStartDate(): \DateTime + { + return $this->startDate; + } + + public function setStartDate(\DateTime $startDate): Result + { + $this->startDate = $startDate; + + return $this; + } + + public function getUserIp(): string + { + return $this->userIp; + } + + public function setUserIp(string $userIp): Result + { + $this->userIp = $userIp; + + return $this; + } + + public function getLtiLaunchId(): string + { + return $this->ltiLaunchId; + } + + public function setLtiLaunchId(string $ltiLaunchId): Result + { + $this->ltiLaunchId = $ltiLaunchId; + + return $this; + } +} diff --git a/plugin/lti_provider/LtiProviderPlugin.php b/plugin/lti_provider/LtiProviderPlugin.php new file mode 100644 index 000000000..cda87a105 --- /dev/null +++ b/plugin/lti_provider/LtiProviderPlugin.php @@ -0,0 +1,545 @@ + + */ +class LtiProviderPlugin extends Plugin +{ + public const TABLE_PLATFORM = 'plugin_lti_provider_platform'; + public const LAUNCH_PATH = 'lti_provider/tool/start.php'; + public const LOGIN_PATH = 'lti_provider/tool/login.php'; + public const REDIRECT_PATH = 'lti_provider/tool/start.php'; + public const JWKS_URL = 'lti_provider/tool/jwks.php'; + + public $isAdminPlugin = true; + + protected function __construct() + { + $version = '1.1'; + $author = 'Christian Beeznest'; + + $message = Display::return_message($this->get_lang('Description')); + + $launchUrlHtml = ''; + $loginUrlHtml = ''; + $redirectUrlHtml = ''; + $jwksUrlHtml = ''; + + if ($this->areTablesCreated()) { + $publicKey = $this->getPublicKey(); + + $pkHtml = $this->getSettingHtmlReadOnly( + $this->get_lang('PublicKey'), + 'public_key', + $publicKey + ); + $launchUrlHtml = $this->getSettingHtmlReadOnly( + $this->get_lang('LaunchUrl'), + 'launch_url', + api_get_path(WEB_PLUGIN_PATH).self::LAUNCH_PATH + ); + $loginUrlHtml = $this->getSettingHtmlReadOnly( + $this->get_lang('LoginUrl'), + 'login_url', + api_get_path(WEB_PLUGIN_PATH).self::LOGIN_PATH + ); + $redirectUrlHtml = $this->getSettingHtmlReadOnly( + $this->get_lang('RedirectUrl'), + 'redirect_url', + api_get_path(WEB_PLUGIN_PATH).self::REDIRECT_PATH + ); + $jwksUrlHtml = $this->getSettingHtmlReadOnly( + $this->get_lang('KeySetUrlJwks'), + 'jwks_url', + api_get_path(WEB_PLUGIN_PATH).self::JWKS_URL + ); + } else { + $pkHtml = $this->get_lang('GenerateKeyPairInfo'); + } + + $settings = [ + $message => 'html', + 'name' => 'hidden', + $launchUrlHtml => 'html', + $loginUrlHtml => 'html', + $redirectUrlHtml => 'html', + $jwksUrlHtml => 'html', + $pkHtml => 'html', + 'enabled' => 'boolean', + ]; + parent::__construct($version, $author, $settings); + } + + /** + * Get the value by default and readonly for the configuration html form. + * + * @param $label + * @param $id + * @param $value + * + * @return string + */ + public function getSettingHtmlReadOnly($label, $id, $value) + { + $html = '
    + +
    +
    '.$value.'
    +
    +
    + +
    '; + + return $html; + } + + /** + * Get a selectbox with quizzes in courses , used for a tool provider. + * + * @param null $clientId + * + * @return string + */ + public function getQuizzesSelect($clientId = null) + { + $courses = CourseManager::get_courses_list(); + $toolProvider = $this->getToolProvider($clientId); + $htmlcontent = '
    + +
    + "; + $htmlcontent .= '
    +
    +
    '; + + return $htmlcontent; + } + + /** + * Get a selectbox with quizzes in courses , used for a tool provider. + * + * @param null $clientId + * + * @return string + */ + public function getLearnPathsSelect($clientId = null) + { + $courses = CourseManager::get_courses_list(); + $toolProvider = $this->getToolProvider($clientId); + $htmlcontent = ''; + + return $htmlcontent; + } + + /** + * Get the public key. + */ + public function getPublicKey(): string + { + $publicKey = ''; + $platformKey = Database::getManager() + ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey') + ->findOneBy([]); + + if ($platformKey) { + $publicKey = $platformKey->getPublicKey(); + } + + return $publicKey; + } + + /** + * Get the first access date of a user in a tool. + * + * @param $courseCode + * @param $toolId + * @param $userId + * + * @return string + */ + public function getUserFirstAccessOnToolLp($courseCode, $toolId, $userId) + { + $dql = "SELECT + a.startDate + FROM ChamiloPluginBundle:LtiProvider\Result a + WHERE + a.courseCode = '$courseCode' AND + a.toolName = 'lp' AND + a.toolId = $toolId AND + a.userId = $userId + ORDER BY a.startDate"; + $qb = Database::getManager()->createQuery($dql); + $result = $qb->getArrayResult(); + + $firstDate = ''; + if (isset($result[0])) { + $startDate = $result[0]['startDate']; + $firstDate = $startDate->format('Y-m-d H:i'); + } + + return $firstDate; + } + + /** + * Get the results of users in tools lti. + * + * @param $startDate + * @param $endDate + * + * @return array + */ + public function getToolLearnPathResult($startDate, $endDate) + { + $dql = "SELECT + a.issuer, + count(DISTINCT(a.userId)) as cnt + FROM + ChamiloPluginBundle:LtiProvider\Result a + WHERE + a.toolName = 'lp' AND + a.startDate BETWEEN '$startDate' AND '$endDate' + GROUP BY a.issuer"; + $qb = Database::getManager()->createQuery($dql); + $issuersValues = $qb->getResult(); + + $result = []; + if (!empty($issuersValues)) { + foreach ($issuersValues as $issuerValue) { + $issuer = $issuerValue['issuer']; + $dqlLp = "SELECT + a.toolId, + a.userId, + a.courseCode + FROM + ChamiloPluginBundle:LtiProvider\Result a + WHERE + a.toolName = 'lp' AND + a.startDate BETWEEN '$startDate' AND '$endDate' AND + a.issuer = '".$issuer."' + GROUP BY a.toolId, a.userId"; + $qbLp = Database::getManager()->createQuery($dqlLp); + $lpValues = $qbLp->getResult(); + + $lps = []; + foreach ($lpValues as $lp) { + $uinfo = api_get_user_info($lp['userId']); + $firstAccess = self::getUserFirstAccessOnToolLp($lp['courseCode'], $lp['toolId'], $lp['userId']); + $lps[$lp['toolId']]['users'][$lp['userId']] = [ + 'firstname' => $uinfo['firstname'], + 'lastname' => $uinfo['lastname'], + 'first_access' => $firstAccess, + ]; + } + $result[] = [ + 'issuer' => $issuer, + 'count_iss_users' => $issuerValue['cnt'], + 'learnpaths' => $lps, + ]; + } + } + + return $result; + } + + /** + * Get the tool provider. + */ + public function getToolProvider($clientId): string + { + $toolProvider = ''; + $platform = Database::getManager() + ->getRepository('ChamiloPluginBundle:LtiProvider\Platform') + ->findOneBy(['clientId' => $clientId]); + + if ($platform) { + $toolProvider = $platform->getToolProvider(); + } + + return $toolProvider; + } + + public function getToolProviderVars($clientId): array + { + $toolProvider = $this->getToolProvider($clientId); + list($courseCode, $tool) = explode('@@', $toolProvider); + list($toolName, $toolId) = explode('-', $tool); + $vars = ['courseCode' => $courseCode, 'toolName' => $toolName, 'toolId' => $toolId]; + + return $vars; + } + + /** + * Get the class instance. + * + * @staticvar LtiProviderPlugin $result + */ + public static function create(): LtiProviderPlugin + { + static $result = null; + + return $result ?: $result = new self(); + } + + /** + * Check whether the current user is a teacher in this context. + */ + public static function isInstructor() + { + api_is_allowed_to_edit(false, true); + } + + /** + * Get the plugin directory name. + */ + public function get_name(): string + { + return 'lti_provider'; + } + + /** + * Install the plugin. Set the database up. + * + * @throws \Doctrine\ORM\Tools\ToolsException + */ + public function install() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_PLATFORM])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(Platform::class), + $em->getClassMetadata(PlatformKey::class), + $em->getClassMetadata(Result::class), + ] + ); + } + + /** + * Save configuration for plugin. + * + * Generate a new key pair for platform when enabling plugin. + * + * @throws OptimisticLockException + * @throws \Doctrine\ORM\ORMException + * + * @return $this|Plugin + */ + public function performActionsAfterConfigure() + { + $em = Database::getManager(); + + /** @var PlatformKey $platformKey */ + $platformKey = $em + ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey') + ->findOneBy([]); + + if ($this->get('enabled') === 'true') { + if (!$platformKey) { + $platformKey = new PlatformKey(); + } + + $keyPair = self::generatePlatformKeys(); + + $platformKey->setKid($keyPair['kid']); + $platformKey->publicKey = $keyPair['public']; + $platformKey->setPrivateKey($keyPair['private']); + + $em->persist($platformKey); + } else { + if ($platformKey) { + $em->remove($platformKey); + } + } + + $em->flush(); + + return $this; + } + + /** + * Unistall plugin. Clear the database. + */ + public function uninstall() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist([self::TABLE_PLATFORM])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(Platform::class), + $em->getClassMetadata(PlatformKey::class), + $em->getClassMetadata(Result::class), + ] + ); + } + + public function trimParams(array &$params) + { + foreach ($params as $key => $value) { + $newValue = preg_replace('/\s+/', ' ', $value); + $params[$key] = trim($newValue); + } + } + + public function saveResult($values, $ltiLaunchId = null) + { + $em = Database::getManager(); + if (!empty($ltiLaunchId)) { + $repo = $em->getRepository(Result::class); + + /** @var Result $objResult */ + $objResult = $repo->findOneBy( + [ + 'ltiLaunchId' => $ltiLaunchId, + ] + ); + if ($objResult) { + $objResult->setScore($values['score']); + $objResult->setProgress($values['progress']); + $objResult->setDuration($values['duration']); + $em->persist($objResult); + $em->flush(); + + return $objResult->getId(); + } + } else { + $objResult = new Result(); + $objResult + ->setIssuer($values['issuer']) + ->setUserId($values['user_id']) + ->setClientUId($values['client_uid']) + ->setCourseCode($values['course_code']) + ->setToolId($values['tool_id']) + ->setToolName($values['tool_name']) + ->setScore(0) + ->setProgress(0) + ->setDuration(0) + ->setStartDate(new DateTime()) + ->setUserIp(api_get_real_ip()) + ->setLtiLaunchId($values['lti_launch_id']) + ; + $em->persist($objResult); + $em->flush(); + + return $objResult->getId(); + } + + return false; + } + + private function areTablesCreated(): bool + { + $entityManager = Database::getManager(); + $connection = $entityManager->getConnection(); + + return $connection->getSchemaManager()->tablesExist(self::TABLE_PLATFORM); + } + + /** + * Generate a key pair and key id for the platform. + * + * Return a associative array like ['kid' => '...', 'private' => '...', 'public' => '...']. + */ + private static function generatePlatformKeys(): array + { + // Create the private and public key + $res = openssl_pkey_new( + [ + 'digest_alg' => 'sha256', + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ] + ); + + // Extract the private key from $res to $privateKey + $privateKey = ''; + openssl_pkey_export($res, $privateKey); + + // Extract the public key from $res to $publicKey + $publicKey = openssl_pkey_get_details($res); + + return [ + 'kid' => bin2hex(openssl_random_pseudo_bytes(10)), + 'private' => $privateKey, + 'public' => $publicKey["key"], + ]; + } + + /** + * Get a SimpleXMLElement object with the request received on php://input. + * + * @throws Exception + */ + private function getRequestXmlElement(): ?SimpleXMLElement + { + $request = file_get_contents("php://input"); + + if (empty($request)) { + return null; + } + + return new SimpleXMLElement($request); + } +} diff --git a/plugin/lti_provider/README.md b/plugin/lti_provider/README.md new file mode 100644 index 000000000..f78c61405 --- /dev/null +++ b/plugin/lti_provider/README.md @@ -0,0 +1,84 @@ +Lti/Provider plugin +=== + +Version 1.0 + +> This plugin is meant to be later integrated into Chamilo (in a major version +release). + +The LTI provider feature is only compatible with LTI 1.3 Advantage, and demonstrates the possibility to integrate tools or content from Chamilo into other LMS platforms. +In this case, Chamilo is used as provider , and this plugin allows a student inside a course to play in a breakout game with certain difficulty options (Deep Linkings) which is scored (Assigment and Grade Services) and compared with the other members of the course (NRP Services). + +# Installation + +*Prior to installing/uninstalling this plugin, you will need to make sure the src/Chamilo/PluginBundle/Entity folder is +temporarily writeable by the web server.* + +1. Install the plugin from the Plugins page +2. Enable the plugin from the Lti Provider Plugin Settings page +3. Assign to the Administrator region (will appear on the management page) +4. Add the LTI connection details to try out the little demo app (Configuration page) +5. Configure the LMS platforms for registration and deployment + +To be able to acces LTI content from a different domain in an iframe, the hosting provider will have to enable it by activating this configuration in the app/config/configuration.php file : +``` +// Enable samesite:None parameter for session cookie. +// More info: https://www.chromium.org/updates/same-site +// Also: https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure +$_configuration['security_session_cookie_samesite_none'] = true; +``` + +# DB tables + +These tables are normally created during the activation of the plugin. They are mentioned here for practical purposes. +Note: "kid" means "Key ID", not "child". + +## v1.0 +```sql +CREATE TABLE plugin_lti_provider_platform ( + id int NOT NULL AUTO_INCREMENT, + issuer varchar(255) NOT NULL, + client_id varchar(255) NOT NULL, + kid int(255) NOT NULL, + auth_login_url varchar(255) NOT NULL, + auth_token_url varchar(255) NOT NULL, + key_set_url varchar(255) NOT NULL, + deployment_id varchar(255) NOT NULL, + tool_provider varchar(255) NULL, + PRIMARY KEY(id) +) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; + +CREATE TABLE plugin_lti_provider_platform_key ( + id INT AUTO_INCREMENT NOT NULL, + kid VARCHAR(255) NOT NULL, + public_key LONGTEXT NOT NULL, + private_key LONGTEXT NOT NULL, + PRIMARY KEY(id) +) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; + +CREATE TABLE plugin_lti_provider_result ( + id int(11) NOT NULL AUTO_INCREMENT, + issuer longtext NOT NULL, + user_id int(11) NOT NULL, + client_uid int(11) NOT NULL, + course_code varchar(40) NOT NULL, + tool_id int(11) NOT NULL, + tool_name varchar(255) NOT NULL, + score double NOT NULL, + progress int(11) NOT NULL, + duration int(11) NOT NULL, + start_date datetime NOT NULL, + user_ip varchar(255) NOT NULL, + lti_launch_id varchar(255) NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +``` + +## v1.1 +### Database changes +You need to execute this SQL query in your database after updating your Chamilo after version 1.11.18 if the plugin was already installed before. + +```sql +ALTER TABLE plugin_lti_provider_result MODIFY client_uid varchar(255) NOT NULL; +``` + diff --git a/plugin/lti_provider/admin.php b/plugin/lti_provider/admin.php new file mode 100644 index 000000000..26dc799be --- /dev/null +++ b/plugin/lti_provider/admin.php @@ -0,0 +1,34 @@ +get('enabled') !== 'true') { + api_not_allowed(true); +} + +$em = Database::getManager(); + +$platforms = $em->getRepository('ChamiloPluginBundle:LtiProvider\Platform')->findAll(); + +$interbreadcrumb[] = ['url' => api_get_path(WEB_CODE_PATH).'admin/index.php', 'name' => get_lang('PlatformAdmin')]; + +$htmlHeadXtra[] = api_get_css( + api_get_path(WEB_PLUGIN_PATH).'lti_provider/assets/style.css' +); + +$template = new Template($plugin->get_title()); +$template->assign('platforms', $platforms); + +$content = $template->fetch('lti_provider/view/provider_admin.tpl'); + +$template->assign('header', $plugin->get_title()); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/lti_provider/create.php b/plugin/lti_provider/create.php new file mode 100644 index 000000000..2eaa34db4 --- /dev/null +++ b/plugin/lti_provider/create.php @@ -0,0 +1,59 @@ +build(); + +if ($form->validate()) { + $formValues = $form->exportValues(); + $platform = new Platform(); + $platform->setIssuer($formValues['issuer']); + $platform->setClientId($formValues['client_id']); + $platform->setAuthLoginUrl($formValues['auth_login_url']); + $platform->setAuthTokenUrl($formValues['auth_token_url']); + $platform->setKeySetUrl($formValues['key_set_url']); + $platform->setDeploymentId($formValues['deployment_id']); + $platform->setKid($formValues['kid']); + $toolProvider = (isset($formValues['tool_provider']) ? $formValues['tool_provider'] : $_POST['tool_provider']); + $platform->setToolProvider($toolProvider); + + $em->persist($platform); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('PlatformConnectionAdded'), 'success') + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php'); + exit; +} + +$form->setDefaultValues(); + +$interbreadcrumb[] = ['url' => api_get_path(WEB_CODE_PATH).'admin/index.php', 'name' => get_lang('PlatformAdmin')]; +$interbreadcrumb[] = ['url' => api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php', 'name' => $plugin->get_title()]; + +$pageTitle = $plugin->get_lang('AddPlatform'); + +$template = new Template($pageTitle); +$template->assign('form', $form->returnForm()); + +$content = $template->fetch('lti_provider/view/add.tpl'); + +$template->assign('header', $pageTitle); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/lti_provider/db/lti13_cache.php b/plugin/lti_provider/db/lti13_cache.php new file mode 100644 index 000000000..49233b477 --- /dev/null +++ b/plugin/lti_provider/db/lti13_cache.php @@ -0,0 +1,59 @@ +loadCache(); + + return $this->cache[$key]; + } + + public function cacheLaunchData($key, $jwtBody): Lti13Cache + { + $this->cache[$key] = $jwtBody; + $this->saveCache(); + + return $this; + } + + public function cacheNonce($nonce): Lti13Cache + { + $this->cache['nonce'][$nonce] = true; + $this->saveCache(); + + return $this; + } + + public function checkNonce($nonce): bool + { + $this->loadCache(); + if (!isset($this->cache['nonce'][$nonce])) { + return false; + } + + return true; + } + + private function loadCache() + { + $cache = file_get_contents(api_get_path(SYS_ARCHIVE_PATH).'lti_cache.txt'); + if (empty($cache)) { + file_put_contents(api_get_path(SYS_ARCHIVE_PATH).'lti_cache.txt', '{}'); + $this->cache = []; + } + $this->cache = json_decode($cache, true); + } + + private function saveCache() + { + file_put_contents(api_get_path(SYS_ARCHIVE_PATH).'lti_cache.txt', json_encode($this->cache)); + } +} diff --git a/plugin/lti_provider/db/lti13_cookie.php b/plugin/lti_provider/db/lti13_cookie.php new file mode 100644 index 000000000..6b5afc700 --- /dev/null +++ b/plugin/lti_provider/db/lti13_cookie.php @@ -0,0 +1,45 @@ + time() + $exp, + ]; + + // SameSite none and secure will be required for tools to work inside iframes + $sameSiteOptions = [ + 'samesite' => 'None', + 'secure' => false, + 'httponly' => true, + ]; + + setcookie($name, $value, array_merge($cookieOptions, $sameSiteOptions, $options)); + + // Set a second fallback cookie in the event that "SameSite" is not supported + setcookie("LEGACY_".$name, $value, array_merge($cookieOptions, $options)); + + return $this; + } +} diff --git a/plugin/lti_provider/db/lti13_database.php b/plugin/lti_provider/db/lti13_database.php new file mode 100644 index 000000000..0ba0c3792 --- /dev/null +++ b/plugin/lti_provider/db/lti13_database.php @@ -0,0 +1,91 @@ +getClientIdByIssuer($iss); + } + + $ltiCustomers = $this->getLtiConnection(); + if (empty($ltiCustomers[$clientId])) { + return false; + } + + return LtiRegistration::new() + ->setAuthLoginUrl($ltiCustomers[$clientId]['auth_login_url']) + ->setAuthTokenUrl($ltiCustomers[$clientId]['auth_token_url']) + ->setClientId($clientId) + ->setKeySetUrl($ltiCustomers[$clientId]['key_set_url']) + ->setKid($ltiCustomers[$clientId]['kid']) + ->setIssuer($iss) + ->setToolPrivateKey($this->getPrivateKey()); + } + + public function findDeployment($iss, $deploymentId, $clientId = null) + { + $issSession = Session::read('iss'); + if (!in_array($deploymentId, $issSession[$clientId]['deployment'])) { + return false; + } + + return LtiDeployment::new()->setDeploymentId($deploymentId); + } + + private function getLtiConnection(): array + { + $em = Database::getManager(); + $platforms = $em->getRepository('ChamiloPluginBundle:LtiProvider\Platform')->findAll(); + + $ltiCustomers = []; + foreach ($platforms as $platform) { + $clientId = $platform->getClientId(); + $ltiCustomers[$clientId] = [ + 'client_id' => $clientId, + 'issuer' => $platform->getIssuer(), + 'auth_login_url' => $platform->getAuthLoginUrl(), + 'auth_token_url' => $platform->getAuthTokenUrl(), + 'key_set_url' => $platform->getKeySetUrl(), + 'kid' => $platform->getKid(), + 'deployment' => [$platform->getDeploymentId()], + ]; + } + Session::write('iss', $ltiCustomers); + + return $ltiCustomers; + } + + private function getClientIdByIssuer($issuer) + { + $clientId = ''; + $platform = Database::getManager() + ->getRepository('ChamiloPluginBundle:LtiProvider\Platform') + ->findOneBy(['issuer' => $issuer]); + + if ($platform) { + $clientId = $platform->getClientId(); + } + + return $clientId; + } + + private function getPrivateKey() + { + $privateKey = ''; + $platformKey = Database::getManager() + ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey') + ->findOneBy([]); + if ($platformKey) { + $privateKey = $platformKey->getPrivateKey(); + } + + return $privateKey; + } +} diff --git a/plugin/lti_provider/delete.php b/plugin/lti_provider/delete.php new file mode 100644 index 000000000..a8cfff1b8 --- /dev/null +++ b/plugin/lti_provider/delete.php @@ -0,0 +1,32 @@ +find('ChamiloPluginBundle:LtiProvider\Platform', $platformId); + +$em->remove($platform); +$em->flush(); + +Display::addFlash( + Display::return_message($plugin->get_lang('PlatformDeleted'), 'success') +); + +header('Location: '.api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php'); +exit; diff --git a/plugin/lti_provider/edit.php b/plugin/lti_provider/edit.php new file mode 100644 index 000000000..bfaf94ff7 --- /dev/null +++ b/plugin/lti_provider/edit.php @@ -0,0 +1,73 @@ +find('ChamiloPluginBundle:LtiProvider\Platform', $platformId); + +if (!$platform) { + Display::addFlash( + Display::return_message($plugin->get_lang('NoPlatform'), 'error') + ); + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php'); + exit; +} + +$form = new FrmEdit('lti_provider_edit_platform', [], $platform); +$form->build(); + +if ($form->validate()) { + $formValues = $form->exportValues(); + $platform->setIssuer($formValues['issuer']); + $platform->setClientId($formValues['client_id']); + $platform->setAuthLoginUrl($formValues['auth_login_url']); + $platform->setAuthTokenUrl($formValues['auth_token_url']); + $platform->setKeySetUrl($formValues['key_set_url']); + $platform->setDeploymentId($formValues['deployment_id']); + $platform->setKid($formValues['kid']); + $toolProvider = (isset($formValues['tool_provider']) ? $formValues['tool_provider'] : $_POST['tool_provider']); + $platform->setToolProvider($toolProvider); + + $em->persist($platform); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('PlatformEdited'), 'success') + ); + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php'); + exit; +} else { + $form->setDefaultValues(); +} + +$interbreadcrumb[] = ['url' => api_get_path(WEB_CODE_PATH).'admin/index.php', 'name' => get_lang('PlatformAdmin')]; +$interbreadcrumb[] = ['url' => api_get_path(WEB_PLUGIN_PATH).'lti_provider/admin.php', 'name' => $plugin->get_title()]; + +$template = new Template($plugin->get_lang('EditPlatform')); +$template->assign('form', $form->returnForm()); + +$content = $template->fetch('lti_provider/view/add.tpl'); + +$template->assign('header', $plugin->get_title()); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/lti_provider/install.php b/plugin/lti_provider/install.php new file mode 100644 index 000000000..25256c7f9 --- /dev/null +++ b/plugin/lti_provider/install.php @@ -0,0 +1,16 @@ +install(); diff --git a/plugin/lti_provider/lang/english.php b/plugin/lti_provider/lang/english.php new file mode 100644 index 000000000..9856a3307 --- /dev/null +++ b/plugin/lti_provider/lang/english.php @@ -0,0 +1,34 @@ +get_info(); diff --git a/plugin/lti_provider/provider_settings.php b/plugin/lti_provider/provider_settings.php new file mode 100644 index 000000000..c9679aff4 --- /dev/null +++ b/plugin/lti_provider/provider_settings.php @@ -0,0 +1,61 @@ +' + .'
    '.$plugin->get_lang('LaunchUrl').'
    ' + .'
    '.$launchUrl.'
    ' + .'
    ' + .'
    ' + .'
    '.$plugin->get_lang('LoginUrl').'
    ' + .'
    '.$loginUrl.'
    ' + .'
    ' + .'
    ' + .'
    '.$plugin->get_lang('RedirectUrl').'
    ' + .'
    '.$redirectUrl.'
    ' + .'
    ' + .'
    ' + .'
    '.$plugin->get_lang('KeySetUrlJwks').'
    ' + .'
    '.$jwksUrl.'
    ' + .'
    '; + + $response->setContent($html); +} catch (Exception $exception) { + $response->setContent( + Display::return_message($exception->getMessage(), 'error') + ); +} + +$response->send(); diff --git a/plugin/lti_provider/src/Form/FrmAdd.php b/plugin/lti_provider/src/Form/FrmAdd.php new file mode 100644 index 000000000..8bc095c40 --- /dev/null +++ b/plugin/lti_provider/src/Form/FrmAdd.php @@ -0,0 +1,88 @@ +platform = $platform; + } + + /** + * Build the form. + */ + public function build(): void + { + $plugin = LtiProviderPlugin::create(); + + $this->addHeader($plugin->get_lang('ConnectionDetails')); + + $this->addText('issuer', $plugin->get_lang('PlatformName')); + $this->addUrl('auth_login_url', $plugin->get_lang('AuthLoginUrl')); + $this->addUrl('auth_token_url', $plugin->get_lang('AuthTokenUrl')); + $this->addUrl('key_set_url', $plugin->get_lang('KeySetUrl')); + $this->addText('client_id', $plugin->get_lang('ClientId')); + $this->addText('deployment_id', $plugin->get_lang('DeploymentId')); + $this->addText('kid', $plugin->get_lang('KeyId'), false); + + $this->addRadio( + 'tool_type', + get_lang('ToolProvider'), + [ + 'quiz' => $plugin->get_lang('Quizzes'), + 'lp' => $plugin->get_lang('Learnpaths'), + ], + [ + 'onclick' => 'selectToolProvider(this.value)', + ] + ); + + $this->addElement('html', $plugin->getLearnPathsSelect()); + $this->addElement('html', $plugin->getQuizzesSelect()); + + $this->addButtonCreate($plugin->get_lang('AddPlatform')); + $this->applyFilter('__ALL__', 'trim'); + } + + public function setDefaultValues(): void + { + $defaults = []; + + if (!$this->platform) { + $this->platform = new Platform(); + } + + $defaults['issuer'] = $this->platform->getIssuer(); + $defaults['auth_login_url'] = $this->platform->getAuthLoginUrl(); + $defaults['auth_token_url'] = $this->platform->getAuthTokenUrl(); + $defaults['key_set_url'] = $this->platform->getKeySetUrl(); + $defaults['client_id'] = $this->platform->getClientId(); + $defaults['deployment_id'] = $this->platform->getDeploymentId(); + $defaults['kid'] = $this->platform->getKid(); + $defaults['tool_type'] = 'quiz'; + + $this->setDefaults($defaults); + } +} diff --git a/plugin/lti_provider/src/Form/FrmEdit.php b/plugin/lti_provider/src/Form/FrmEdit.php new file mode 100644 index 000000000..57a583ea1 --- /dev/null +++ b/plugin/lti_provider/src/Form/FrmEdit.php @@ -0,0 +1,94 @@ +platform = $platform; + } + + /** + * Build the form. + */ + public function build() + { + $plugin = LtiProviderPlugin::create(); + $this->addHeader($plugin->get_lang('ConnectionDetails')); + + $this->addText('issuer', $plugin->get_lang('PlatformName')); + $this->addUrl('auth_login_url', $plugin->get_lang('AuthLoginUrl')); + $this->addUrl('auth_token_url', $plugin->get_lang('AuthTokenUrl')); + $this->addUrl('key_set_url', $plugin->get_lang('KeySetUrl')); + $this->addText('client_id', $plugin->get_lang('ClientId')); + $this->addText('deployment_id', $plugin->get_lang('DeploymentId')); + $this->addText('kid', $plugin->get_lang('KeyId'), false); + + $this->addRadio( + 'tool_type', + get_lang('ToolProvider'), + [ + 'quiz' => $plugin->get_lang('Quizzes'), + 'lp' => $plugin->get_lang('Learnpaths'), + ], + [ + 'onclick' => 'selectToolProvider(this.value)', + ] + ); + + $this->addElement('html', $plugin->getLearnPathsSelect($this->platform->getClientId())); + $this->addElement('html', $plugin->getQuizzesSelect($this->platform->getClientId())); + + $this->addButtonCreate($plugin->get_lang('EditPlatform')); + $this->addHidden('id', $this->platform->getId()); + $this->addHidden('action', 'edit'); + $this->applyFilter('__ALL__', 'trim'); + } + + /** + * @throws Exception + */ + public function setDefaultValues(): void + { + $defaults = []; + $defaults['issuer'] = $this->platform->getIssuer(); + $defaults['auth_login_url'] = $this->platform->getAuthLoginUrl(); + $defaults['auth_token_url'] = $this->platform->getAuthTokenUrl(); + $defaults['key_set_url'] = $this->platform->getKeySetUrl(); + $defaults['client_id'] = $this->platform->getClientId(); + $defaults['deployment_id'] = $this->platform->getDeploymentId(); + $defaults['kid'] = $this->platform->getKid(); + + $toolProvider = $this->platform->getToolProvider(); + list($courseCode, $tool) = explode('@@', $toolProvider); + list($toolName, $toolId) = explode('-', $tool); + + $defaults['tool_type'] = $toolName; + $defaults['tool_provider'] = $toolProvider; + + $this->setDefaults($defaults); + } +} diff --git a/plugin/lti_provider/src/LtiProvider.php b/plugin/lti_provider/src/LtiProvider.php new file mode 100644 index 000000000..dd77548c1 --- /dev/null +++ b/plugin/lti_provider/src/LtiProvider.php @@ -0,0 +1,170 @@ +doOidcLoginRedirect($launchUrl, $request) + ->doRedirect(); + } + + /** + * It removes user and oLP session. + */ + public function logout(string $toolName = '') + { + Session::erase('_user'); + Session::erase('is_platformAdmin'); + Session::erase('is_allowedCreateCourse'); + Session::erase('_uid'); + if ('lp' == $toolName) { + // Deleting the objects + Session::erase('oLP'); + Session::erase('lpobject'); + Session::erase('scorm_view_id'); + Session::erase('scorm_item_id'); + Session::erase('exerciseResult'); + Session::erase('objExercise'); + Session::erase('questionList'); + } + Session::erase('is_allowed_in_course'); + Session::erase('_real_cid'); + Session::erase('_cid'); + Session::erase('_course'); + } + + /** + * Lti Message Launch. + */ + public function launch(bool $fromCache = false, ?string $launchId = null): LtiMessageLaunch + { + if ($fromCache) { + $launch = LtiMessageLaunch::fromCache($launchId, new Lti13Database(), new Lti13Cache()); + } else { + $launch = LtiMessageLaunch::new(new Lti13Database(), new Lti13Cache(), new Lti13Cookie())->validate(); + } + + return $launch; + } + + /** + * Verify if user is in the provider platform to create it and login (true) or not (false). + */ + public function validateUser(array $launchData, string $courseCode, string $toolName): bool + { + if (empty($launchData)) { + return false; + } + + $authSource = IMS_LTI_SOURCE; + $username = md5($launchData['iss'].'_'.$launchData['sub']); + $userInfo = api_get_user_info_from_username($username, $authSource); + if (empty($userInfo)) { + $email = $username.'@'.$authSource.'.com'; + if (!empty($launchData['email'])) { + $email = $launchData['email']; + } + $firstName = $launchData['aud']; + if (!empty($launchData['given_name'])) { + $firstName = $launchData['given_name']; + } + $lastName = $launchData['sub']; + if (!empty($launchData['family_name'])) { + $lastName = $launchData['family_name']; + } + $password = api_generate_password(); + $userId = UserManager::create_user( + $firstName, + $lastName, + STUDENT, + $email, + $username, + $password, + '', + '', + '', + '', + $authSource + ); + } else { + $userId = $userInfo['user_id']; + } + + if (!CourseManager::is_user_subscribed_in_course($userId, $courseCode)) { + CourseManager::subscribeUser($userId, $courseCode); + } + + $this->logout($toolName); + + $login = UserManager::loginAsUser($userId, false); + if ($login && CourseManager::is_user_subscribed_in_course($userId, $courseCode)) { + $_course = api_get_course_info($courseCode); + Session::write('is_allowed_in_course', true); + Session::write('_real_cid', $_course['real_id']); + Session::write('_cid', $_course['code']); + Session::write('_course', $_course); + } + + return $login; + } + + /** + * It checks if request is from lti customer. + * + * @param $request + * @param $session + * + * @return bool + */ + public function isLtiRequest($request, $session) + { + $isLti = false; + if (isset($request['lti_message_hint'])) { + $isLti = true; + } elseif (isset($request['state'])) { + $isLti = true; + } elseif (isset($request['lti_launch_id']) && 'learnpath' === api_get_origin()) { + $isLti = true; + } elseif (isset($request['lti_launch_id'])) { + $isLti = true; + } elseif (isset($session['oLP']->lti_launch_id)) { + $isLti = true; + } + + return $isLti; + } +} diff --git a/plugin/lti_provider/tool/api/score.php b/plugin/lti_provider/tool/api/score.php new file mode 100644 index 000000000..a84e44906 --- /dev/null +++ b/plugin/lti_provider/tool/api/score.php @@ -0,0 +1,61 @@ +launch(true, $_REQUEST['launch_id']); + +if (!$launch->hasAgs()) { + throw new Exception("Don't have grades!"); +} + +if (!isset($_REQUEST['lti_result_id'])) { + throw new Exception("Any tool result"); +} + +$launchData = $launch->getLaunchData(); + +$courseCode = $_REQUEST['cidReq']; +$courseId = api_get_course_int_id($courseCode); +$toolName = $_REQUEST['lti_tool']; + +if (in_array($toolName, ['quiz', 'lp'])) { + if ('quiz' == $toolName) { + $objExercise = new Exercise($courseId); + $trackInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($_REQUEST['lti_result_id']); + $score = $trackInfo['exe_result']; + $weight = $trackInfo['exe_weighting']; + $timestamp = date(DATE_ISO8601); + } else { + $lpId = (int) $_REQUEST['lti_result_id']; + $lpProgress = learnpath::getProgress( + $lpId, + api_get_user_id(), + api_get_course_int_id(), + api_get_session_id() + ); + $score = $lpProgress; + $weight = 100; + $timestamp = date(DATE_ISO8601); + } + + $grades = $launch->getAgs(); + $scoreGrade = Packback\Lti1p3\LtiGrade::new() + ->setScoreGiven($score) + ->setScoreMaximum($weight) + ->setTimestamp($timestamp) + ->setActivityProgress('Completed') + ->setGradingProgress('FullyGraded') + ->setUserId($launchData['sub']); + $grades->putGrade($scoreGrade); + + $plugin = LtiProviderPlugin::create(); + $values = []; + $values['score'] = $score; + $values['progress'] = 0; + $values['duration'] = 0; + $plugin->saveResult($values, $_REQUEST['launch_id']); + + echo '{"success" : true}'; +} diff --git a/plugin/lti_provider/tool/jwks.php b/plugin/lti_provider/tool/jwks.php new file mode 100644 index 000000000..d582eee39 --- /dev/null +++ b/plugin/lti_provider/tool/jwks.php @@ -0,0 +1,49 @@ +get('enabled')) { + exit; +} + +/** @var PlatformKey $platformKey */ +$platformKey = Database::getManager() + ->getRepository('ChamiloPluginBundle:LtiProvider\PlatformKey') + ->findOneBy([]); + +if (!$platformKey) { + exit; +} + +$privateKey = $platformKey->getPrivateKey(); + +$jwks = []; + +$key = new RSA(); +$key->setHash('sha256'); +$key->loadKey($platformKey->getPrivateKey()); +$key->setPublicKey(false, RSA::PUBLIC_FORMAT_PKCS8); + +if ($key->publicExponent) { + $jwks = [ + 'kty' => 'RSA', + 'alg' => 'RS256', + 'use' => 'sig', + 'e' => JWT::urlsafeB64Encode($key->publicExponent->toBytes()), + 'n' => JWT::urlsafeB64Encode($key->modulus->toBytes()), + 'kid' => $platformKey->getKid(), + ]; +} + +header('Content-Type: application/json'); + +echo json_encode(['keys' => [$jwks]]); diff --git a/plugin/lti_provider/tool/login.php b/plugin/lti_provider/tool/login.php new file mode 100644 index 000000000..6b431593a --- /dev/null +++ b/plugin/lti_provider/tool/login.php @@ -0,0 +1,7 @@ +login($_REQUEST); diff --git a/plugin/lti_provider/tool/logout.php b/plugin/lti_provider/tool/logout.php new file mode 100644 index 000000000..63a39f56d --- /dev/null +++ b/plugin/lti_provider/tool/logout.php @@ -0,0 +1,10 @@ +logout(); +header('Location: '.api_get_path(WEB_PATH)); +exit; diff --git a/plugin/lti_provider/tool/start.php b/plugin/lti_provider/tool/start.php new file mode 100644 index 000000000..32b7f0edb --- /dev/null +++ b/plugin/lti_provider/tool/start.php @@ -0,0 +1,44 @@ +launch(); +if (!$launch->hasNrps()) { + // throw new Exception("Don't have names and roles!"); +} + +$launchData = $launch->getLaunchData(); + +$plugin = LtiProviderPlugin::create(); +$toolVars = $plugin->getToolProviderVars($launchData['aud']); + +$login = LtiProvider::create()->validateUser($launchData, $toolVars['courseCode'], $toolVars['toolName']); +$ltiSession = []; +if ($login) { + $values = []; + $values['issuer'] = $launchData['iss']; + $values['user_id'] = api_get_user_id(); + $values['client_uid'] = $launchData['sub']; + $values['course_code'] = $toolVars['courseCode']; + $values['tool_id'] = $toolVars['toolId']; + $values['tool_name'] = $toolVars['toolName']; + $values['lti_launch_id'] = $launch->getLaunchId(); + $plugin->saveResult($values); + $ltiSession = $values; +} + +$cidReq = 'cidReq='.$toolVars['courseCode'].'&id_session=0&gidReq=0&gradebook=0'; + +if ('lp' == $toolVars['toolName']) { + $launchUrl = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.$cidReq.'&action=view&lp_id='.$toolVars['toolId'].'&isStudentView=true<i_launch_id='.$launch->getLaunchId(); +} else { + $launchUrl = api_get_path(WEB_CODE_PATH).'exercise/overview.php?'.$cidReq.'&origin=embeddable&exerciseId='.$toolVars['toolId'].'<i_launch_id='.$launch->getLaunchId(); +} +$ltiSession['launch_url'] = $launchUrl; +Session::write('_ltiProvider', $ltiSession); +header('Location: '.$launchUrl); +exit; diff --git a/plugin/lti_provider/uninstall.php b/plugin/lti_provider/uninstall.php new file mode 100644 index 000000000..e84a040f3 --- /dev/null +++ b/plugin/lti_provider/uninstall.php @@ -0,0 +1,16 @@ +uninstall(); diff --git a/plugin/lti_provider/view/add.tpl b/plugin/lti_provider/view/add.tpl new file mode 100644 index 000000000..30c37490b --- /dev/null +++ b/plugin/lti_provider/view/add.tpl @@ -0,0 +1,19 @@ +
    +
    + {{ form }} +
    +
    + diff --git a/plugin/lti_provider/view/provider_admin.tpl b/plugin/lti_provider/view/provider_admin.tpl new file mode 100644 index 000000000..acaffc00d --- /dev/null +++ b/plugin/lti_provider/view/provider_admin.tpl @@ -0,0 +1,52 @@ + +{{ 'LtiProviderDescription'|get_plugin_lang('LtiProviderPlugin') }} + + + +
    + + + + + + + + + + + + + {% for platform in platforms %} + {% set url_params = {'id': platform.getId}|url_encode() %} + + + + + + + + + {% endfor %} + + +
    {{ 'PlatformName'|get_plugin_lang('LtiProviderPlugin') }}{{ 'ClientId'|get_plugin_lang('LtiProviderPlugin') }}{{ 'DeploymentId'|get_plugin_lang('LtiProviderPlugin') }}{{ 'URLs'|get_plugin_lang('LtiProviderPlugin') }}{{ 'ToolProvider'|get_plugin_lang('LtiProviderPlugin') }}{{ 'Actions'|get_lang }}
    {{ platform.getIssuer }}{{ platform.getClientId }}{{ platform.getDeploymentId }} +

    {{ 'AuthLoginUrl'|get_plugin_lang('LtiProviderPlugin') }}:
    {{ platform.getAuthLoginUrl }}

    +

    {{ 'AuthTokenUrl'|get_plugin_lang('LtiProviderPlugin') }}:
    {{ platform.getAuthTokenUrl }}

    +

    {{ 'KeySetUrl'|get_plugin_lang('LtiProviderPlugin') }}:
    {{ platform.getKeySetUrl }}

    +
    {{ platform.getToolProvider }} + + {{ 'edit.png'|img(22, 'Edit'|get_lang) }} + + + {{ 'delete.png'|img(22, 'Delete'|get_lang) }} + +
    +
    + diff --git a/plugin/maintenancemode/MaintenanceModePlugin.php b/plugin/maintenancemode/MaintenanceModePlugin.php new file mode 100644 index 000000000..1b8d5ee95 --- /dev/null +++ b/plugin/maintenancemode/MaintenanceModePlugin.php @@ -0,0 +1,32 @@ + 'boolean', + ] + ); + } + + /** + * @return $this + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } +} diff --git a/plugin/maintenancemode/README.md b/plugin/maintenancemode/README.md new file mode 100644 index 000000000..c00e87643 --- /dev/null +++ b/plugin/maintenancemode/README.md @@ -0,0 +1,9 @@ +Maintenance Mode plugin +=== + +This plugin allows administrators to set the portal in maintenance mode through the change of the .htaccess file located +in the root folder of Chamilo. + +As such, it requires the web server (user www-data, httpd or nobody) to temporarily have write access to the .htaccess +file. Maintaining write access on this file is a security vulnerability, so please only set those permissions for the +required time to put your site in maintenance mode, and then return them to non-writable by the web server. \ No newline at end of file diff --git a/plugin/maintenancemode/index.php b/plugin/maintenancemode/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/maintenancemode/index.php @@ -0,0 +1 @@ +get_info(); + +$isPlatformAdmin = api_is_platform_admin(); +$editFile = false; + +$file = api_get_path(SYS_PATH).'.htaccess'; +$maintenanceHtml = api_get_path(SYS_PATH).'maintenance.html'; + +if ($plugin->isEnabled() && $isPlatformAdmin) { + if (!file_exists($file)) { + Display::addFlash( + Display::return_message( + "$file does not exists. ", + 'warning' + ) + ); + } else { + if (is_readable($file) && is_writable($file)) { + $editFile = true; + } else { + if (!is_readable($file)) { + Display::addFlash( + Display::return_message("$file is not readable", 'warning') + ); + } + + if (!is_writable($file)) { + Display::addFlash( + Display::return_message("$file is not writable", 'warning') + ); + } + } + } +} + +if ($editFile && $isPlatformAdmin) { + $originalContent = file_get_contents($file); + $beginLine = '###@@ This part was generated by the edit_htaccess plugin @@##'; + $endLine = '###@@ End @@##'; + + $handler = fopen($file, 'r'); + $deleteLinesList = []; + $deleteLine = false; + $contentNoBlock = ''; + $block = ''; + while (!feof($handler)) { + $line = fgets($handler); + $lineTrimmed = trim($line); + + if ($lineTrimmed == $beginLine) { + $deleteLine = true; + } + + if ($deleteLine) { + $block .= $line; + } else { + $contentNoBlock .= $line; + } + + if ($lineTrimmed == $endLine) { + $deleteLine = false; + } + } + + fclose($handler); + $block = str_replace($beginLine, '', $block); + $block = str_replace($endLine, '', $block); + + $form = new FormValidator('htaccess'); + $form->addHtml($plugin->get_lang('TheFollowingTextWillBeAddedToHtaccess')); + $element = $form->addText( + 'ip', + [$plugin->get_lang('IPAdmin'), $plugin->get_lang('IPAdminDescription')] + ); + $element->freeze(); + $form->addTextarea('text', 'htaccess', ['rows' => '15']); + + $config = [ + 'ToolbarSet' => 'Documents', + 'Width' => '100%', + 'Height' => '400', + 'allowedContent' => true, + ]; + + $form->addHtmlEditor( + 'maintenance', + 'Maintenance', + true, + true, + $config + ); + + $form->addCheckBox('active', null, get_lang('Active')); + + $form->addButtonSave(get_lang('Save')); + $content = ''; + if (file_exists($maintenanceHtml)) { + $content = file_get_contents($maintenanceHtml); + } + if (empty($content)) { + $content = ''; + } + + $isActive = api_get_plugin_setting('maintenancemode', 'active'); + + $ip = api_get_real_ip(); + if ($ip == '::1') { + $ip = '127.0.0.1'; + } + $ipSubList = explode('.', $ip); + $implode = implode('\.', $ipSubList); + $append = api_get_configuration_value('url_append'); + + $default = ' +RewriteCond %{REQUEST_URI} !'.$append.'/maintenance.html$ +RewriteCond %{REMOTE_ADDR} !^'.$implode.' +RewriteRule ^\.*$ '.$append.'/maintenance.html [R=302,L] +'; + if (empty($block)) { + $block = $default; + } + + $form->setDefaults([ + 'text' => $block, + 'maintenance' => $content, + 'ip' => $ip, + 'active' => $isActive, + ]); + + if ($form->validate()) { + $values = $form->getSubmitValues(); + $text = $values['text']; + $active = isset($values['active']) ? true : false; + $content = $values['maintenance']; + + // Restore htaccess with out the block + $newFileContent = $beginLine.PHP_EOL; + $newFileContent .= trim($text).PHP_EOL; + $newFileContent .= $endLine; + $newFileContent .= PHP_EOL; + $newFileContent .= $contentNoBlock; + // Remove ^m chars + $newFileContent = str_ireplace("\x0D", '', $newFileContent); + file_put_contents($file, $newFileContent); + + $handle = curl_init(api_get_path(WEB_PATH)); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + $response = curl_exec($handle); + $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); + curl_close($handle); + + $statusOkList = [ + 200, + 301, + 302, + ]; + + if (in_array($httpCode, $statusOkList)) { + $result = file_put_contents($maintenanceHtml, $content); + if ($result === false) { + Display::addFlash( + Display::return_message( + sprintf($plugin->get_lang('MaintenanceFileNotPresent'), $maintenanceHtml), + 'warning' + ) + ); + } + } else { + // Looks htaccess contains errors. Restore as it was. + Display::addFlash( + Display::return_message( + 'Check your htaccess instructions. The original file was restored.', + 'warning' + ) + ); + $originalContent = str_replace("\x0D", '', $originalContent); + file_put_contents($file, $originalContent); + } + + if ($active == false) { + $message = $plugin->get_lang('MaintenanceModeIsOff'); + $contentNoBlock = str_replace("\x0D", '', $contentNoBlock); + file_put_contents($file, $contentNoBlock); + } else { + $message = $plugin->get_lang('MaintenanceModeIsOn'); + } + Display::addFlash(Display::return_message($message)); + } + $plugin_info['settings_form'] = $form; +} diff --git a/plugin/migrationmoodle/README.md b/plugin/migrationmoodle/README.md new file mode 100644 index 000000000..b369b4aa6 --- /dev/null +++ b/plugin/migrationmoodle/README.md @@ -0,0 +1,24 @@ +# MigrationMoodlePlugin + +Allow migrate course contents and user progress from a Moodle platform. + +> In development. + +## Instructions + +- Install the plugin +- Set the configuration (params to moodle DB connection and moodledata directory) +- Optionally, set the admin region + +You can run the migration tasks from browser using the admin panel. +You must ejecute the tasks in the order indicated in the task list. + +Also you can run all the migrations running `php plugin/migrationmoodle/run_cli.php`. +But if you want to run the migration with multiple url, then you will need edi `MigrationMoodlePlugin::getAccessUrlId`, +`MigrationMoodlePlugin::getMoodledataPath` methods to set your plugin settings. + +# Notes + +- Check if exists an index on `c_lp_item_view.status` on Chamilo DB. + It for optimize the performance when executing the SQL query used in UserScormProgressLoader. +- It requires a Moodle DB with MySQL 8 or MariaDB 10.2.2 for some tasks (LessonPagesTask). diff --git a/plugin/migrationmoodle/admin.php b/plugin/migrationmoodle/admin.php new file mode 100644 index 000000000..a7e3a35d5 --- /dev/null +++ b/plugin/migrationmoodle/admin.php @@ -0,0 +1,241 @@ +get('active')) { + api_not_allowed(true); +} + +$menuTasks = [ + '_' => [ + 'course_categories', + 'courses', + //'role_assignments', + 'users', + ], + 'courses' => [ + 'course_introductions', + 'course_sections', + 'course_modules_scorm', + ], + 'course_sections' => [ + 'files_for_course_sections', + 'course_modules_lesson', + 'course_modules_quiz', + 'course_modules_url', + //'c_quiz', + 'sort_section_modules', + ], + 'course_modules_lesson' => [ + 'lesson_pages', + ], + 'lesson_pages' => [ + 'lesson_pages_document', + 'lesson_pages_quiz', + ], + 'lesson_pages_document' => [ + 'files_for_lesson_pages', + ], + 'lesson_pages_quiz' => [ + 'lesson_pages_quiz_question', + 'files_for_lesson_answers', + ], + 'lesson_pages_quiz_question' => [ + 'lesson_answers_true_false', + 'lesson_answers_multiple_choice', + 'lesson_answers_multiple_answer', + 'lesson_answers_matching', + 'lesson_answers_essay', + 'lesson_answers_short_answer', + ], + 'course_modules_quiz' => [ + 'quizzes', + 'quizzes_scores', + ], + 'quizzes' => [ + 'files_for_quizzes', + 'question_categories', + 'questions', + ], + 'questions' => [ + 'question_multi_choice_single', + 'question_multi_choice_multiple', + 'questions_true_false', + 'question_short_answer', + 'question_gapselect', + ], + 'course_modules_scorm' => [ + 'scorm_scoes', + ], + 'scorm_scoes' => [ + 'files_for_scorm_scoes', + ], + 'course_introductions' => [ + 'files_for_course_introductions', + ], + 'course_modules_url' => [ + 'urls', + ], + 'users' => [ + 'users_last_login', + 'track_login', + 'user_sessions', + ], + 'user_sessions' => [ + 'users_learn_paths', + 'users_scorms_view', + 'track_course_access', + ], + 'users_learn_paths' => [ + 'users_learn_paths_lesson_timer', + 'users_learn_paths_lesson_branch', + 'users_learn_paths_lesson_attempts', + 'users_learn_paths_quizzes', + ], + 'users_learn_paths_quizzes' => [ + 'users_quizzes_attempts', + 'user_question_attempts_shortanswer', + 'user_question_attempts_gapselect', + 'user_question_attempts_truefalse', + ], +]; + +$menuScripts = [ + '_' => [ + 'user_learn_paths_progress', + 'user_scorms_progress', + ], +]; + +$htmlHeadXtra[] = ''; + +Display::display_header($plugin->get_title()); + +echo '
    '; +echo '
    '; +echo '
    ';
    +
    +if (!empty($action) && isAllowedAction($action, $menuTasks) && !$plugin->isTaskDone($action)) {
    +    $taskName = api_underscore_to_camel_case($action).'Task';
    +
    +    echo Display::page_subheader(
    +        $plugin->get_lang($taskName)
    +    );
    +
    +    $taskName = 'Chamilo\\PluginBundle\\MigrationMoodle\\Task\\'.$taskName;
    +
    +    /** @var BaseTask $task */
    +    $task = new $taskName();
    +
    +    $task->execute();
    +}
    +
    +if (!empty($action) && isAllowedAction($action, $menuScripts) && !$plugin->isTaskDone($action)) {
    +    $scriptName = api_underscore_to_camel_case($action).'Script';
    +
    +    echo Display::page_subheader(
    +        $plugin->get_lang($scriptName)
    +    );
    +
    +    $scriptClass = 'Chamilo\\PluginBundle\\MigrationMoodle\\Script\\'.$scriptName;
    +
    +    /** @var BaseScript $script */
    +    $script = new $scriptClass();
    +
    +    $script->run();
    +}
    +
    +echo '
    '; +echo '
    '; +echo '
    '; +echo Display::page_subheader('Tasks'); +echo displayMenu($menuTasks); +echo Display::page_subheader('Scripts'); +echo displayMenu($menuScripts, 'Script'); +echo '
    '; +echo '
    '; + +Display::display_footer(); + +/** + * @param string $parent + * @param string $type + * + * @return string + */ +function displayMenu(array $menu, $type = 'Task', $parent = '_') +{ + $plugin = MigrationMoodlePlugin::create(); + + $items = $menu[$parent]; + + $isParentDone = $parent === '_' ? true : $plugin->isTaskDone($parent); + + $baseUrl = api_get_self()."?action="; + + $html = '
      '; + + foreach ($items as $item) { + $title = api_underscore_to_camel_case($item); + + $html .= '
    1. '; + + $htmlItem = Display::returnFontAwesomeIcon('check-square-o', '', true); + $htmlItem .= $plugin->get_lang($title.$type); + + if ($isParentDone) { + if (!$plugin->isTaskDone($item)) { + $htmlItem = Display::returnFontAwesomeIcon('square-o', '', true); + $htmlItem .= Display::url( + $plugin->get_lang($title.$type), + $baseUrl.$item + ); + } + } + + $html .= $htmlItem; + + if (isset($menu[$item])) { + $html .= displayMenu($menu, $type, $item); + } + + $html .= '
    2. '; + } + + $html .= '
    '; + + return $html; +} + +/** + * @param string $action + * + * @return bool + */ +function isAllowedAction($action, array $menu) +{ + foreach ($menu as $items) { + if (in_array($action, $items)) { + return true; + } + } + + return false; +} diff --git a/plugin/migrationmoodle/install.php b/plugin/migrationmoodle/install.php new file mode 100644 index 000000000..75430e8a0 --- /dev/null +++ b/plugin/migrationmoodle/install.php @@ -0,0 +1,59 @@ +get_lang('MoodlePassword'), + '' + ); + + createPluginTables(); +} catch (Exception $exception) { + $message = sprintf( + $plugin->get_lang('InstallError'), + $exception->getMessage() + ); + + echo Display::return_message($message, 'error'); +} + +/** + * Create database tables for this plugin. + */ +function createPluginTables() +{ + $installed = AppPlugin::getInstance()->isInstalled('migrationmoodle'); + + if ($installed) { + return; + } + + $queries = []; + $queries[] = "CREATE TABLE IF NOT EXISTS plugin_migrationmoodle_task ( + id INT AUTO_INCREMENT NOT NULL, + name VARCHAR(255) NOT NULL, + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"; + $queries[] = "CREATE TABLE IF NOT EXISTS plugin_migrationmoodle_item ( + id INT AUTO_INCREMENT NOT NULL, + task_id INT NOT NULL, + hash VARCHAR(255) NOT NULL, + extracted_id INT NOT NULL, + loaded_id INT NOT NULL, + INDEX IDX_HASH (hash), + INDEX IDX_EXTRACTED_LOADED (extracted_id, loaded_id), + INDEX IDX_LOADED (loaded_id), + INDEX IDX_TASK (task_id), + PRIMARY KEY(id) + ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"; + $queries[] = "ALTER TABLE plugin_migrationmoodle_item ADD CONSTRAINT FK_TASK FOREIGN KEY (task_id) + REFERENCES plugin_migrationmoodle_task (id) ON DELETE CASCADE"; + + foreach ($queries as $query) { + Database::query($query); + } +} diff --git a/plugin/migrationmoodle/lang/english.php b/plugin/migrationmoodle/lang/english.php new file mode 100644 index 000000000..d0734bd84 --- /dev/null +++ b/plugin/migrationmoodle/lang/english.php @@ -0,0 +1,79 @@ +' + .'The import of courses is also affected by this configuration, as it only imports courses in which users with this username prefix are enrolled.'; +$strings['url_id'] = 'URL ID'; +$strings['url_id_help'] = 'Access URL ID to save courses, users and sessions to (if using multi-url).'; +$strings['moodle_path'] = 'Moodledata path'; +$strings['moodle_path_help'] = 'The moodledata folder path. Usually something like
    /var/www/moodledata
    '; + +// Tasks +$strings['UsersTask'] = 'Users'; +$strings['CourseCategoriesTask'] = 'Course categories'; +$strings['CoursesTask'] = 'Courses'; +$strings['CourseSectionsTask'] = 'Course sections'; +$strings['CourseModulesLessonTask'] = 'Course modules: Lessons'; +$strings['LessonPagesTask'] = 'Lesson pages'; +$strings['LessonPagesDocumentTask'] = 'Lesson pages: Documents'; +$strings['FilesForLessonPagesTask'] = 'Files for lesson pages'; +$strings['LessonPagesQuizTask'] = 'Lesson pages: Questions'; +$strings['LessonPagesQuizQuestionTask'] = 'Questions for question pages'; +$strings['LessonAnswersTrueFalseTask'] = 'Answers for True/False questions'; +$strings['LessonAnswersMultipleChoiceTask'] = 'Answers for Multiple Choice questions'; +$strings['LessonAnswersMultipleAnswerTask'] = 'Answers for Multiple Answer questions'; +$strings['LessonAnswersMatchingTask'] = 'Answers for Matching questions'; +$strings['LessonAnswersEssayTask'] = 'Answers for Essay questions'; +$strings['LessonAnswersShortAnswerTask'] = 'Answers for Short Answer questions'; +$strings['FilesForLessonAnswersTask'] = 'Files for lesson answers'; +$strings['CourseModulesQuizTask'] = 'Course modules: Quizzes'; +$strings['CQuizTask'] = 'C Quiz'; +$strings['RoleAssignmentsTask'] = 'Role assignments'; +$strings['QuizzesTask'] = 'Quizzes'; +$strings['FilesForQuizzesTask'] = 'Files for quizzes'; +$strings['QuestionCategoriesTask'] = 'Question categories'; +$strings['QuestionsTask'] = 'Questions'; +$strings['QuestionMultiChoiceSingleTask'] = 'Answers for multichoice questions (single)'; +$strings['QuestionMultiChoiceMultipleTask'] = 'Answers for multichoice questions (multiple)'; +$strings['QuestionsTrueFalseTask'] = 'Answers for truefalse questions'; +$strings['QuestionShortAnswerTask'] = 'Answers for shortanswers questions'; +$strings['CourseModulesScormTask'] = 'SCORM courses'; +$strings['ScormScoesTask'] = 'SCORM items'; +$strings['FilesForScormScoesTask'] = 'Files for SCORM items'; +$strings['UserSessionsTask'] = 'Course Sessions for users'; +$strings['CourseIntroductionsTask'] = 'Course introductions'; +$strings['FilesForCourseIntroductionsTask'] = 'Files for course introductions'; +$strings['FilesForCourseSectionsTask'] = 'Files for course sections'; +$strings['CourseModulesUrlTask'] = 'Course modules: URLs'; +$strings['UrlsTask'] = 'URLs'; +$strings['SortSectionModulesTask'] = 'Sort modules in section'; +$strings['UsersScormsViewTask'] = 'SCORM views for users'; +$strings['UsersScormsProgressTask'] = 'SCORM progress'; +$strings['UsersLearnPathsTask'] = 'Learn paths views of users'; +$strings['UsersLearnPathsLessonTimerTask'] = 'Lesson timer to start time of Learn paths section'; +$strings['QuizzesScoresTask'] = 'Update quiz scores in learn path'; +$strings['QuestionGapselectTask'] = 'Answers for gapselect questions'; +$strings['UsersLearnPathsLessonBranchTask'] = 'Lesson branch to total time in learn paths documents'; +$strings['UsersLearnPathsLessonAttemptsTask'] = 'Lesson attempts to total time in learn paths quizzes'; +$strings['UsersLearnPathsQuizzesTask'] = 'Quizzes attempts to learn paths quizzes attempts'; +$strings['UsersQuizzesAttemptsTask'] = 'Quiz attempts of users'; +$strings['UserQuestionAttemptsShortanswerTask'] = 'Question attempts of users for shortanswer'; +$strings['UserQuestionAttemptsGapselectTask'] = 'Question attempts of users for gapselect'; +$strings['UserQuestionAttemptsTruefalseTask'] = 'Question attempts of users for truefalse'; +$strings['UsersLastLoginTask'] = 'Last logins for users'; +$strings['TrackLoginTask'] = 'First login and last logout'; +$strings['TrackCourseAccessTask'] = 'User access to course'; +$strings['UserLearnPathsProgressScript'] = 'Update progress in learning paths for users.'; +$strings['UserScormsProgressScript'] = 'Update progress in SCORMs for users.'; diff --git a/plugin/migrationmoodle/lang/french.php b/plugin/migrationmoodle/lang/french.php new file mode 100644 index 000000000..7c6031ab0 --- /dev/null +++ b/plugin/migrationmoodle/lang/french.php @@ -0,0 +1,24 @@ +' + .'L\'import des cours est aussi affecté par cette variable, car seuls les cours auxquels des utilisateurs commençant par ce préfixe sont inscrits seront migrés.'; +$strings['url_id'] = 'ID d\'URL'; +$strings['url_id_help'] = 'ID d\'URL (si multi-URL utilisés) dans lequel sauver les cours, utiilisateurs et sessions.'; +$strings['moodle_path'] = 'Chemin vers moodledata'; +$strings['moodle_path_help'] = 'Le chemin vers le répertoire moodledata. Habituellement quelque chose comme
    /var/www/moodledata
    '; + +// Tasks +$strings['UsersTask'] = 'Utilisateurs'; diff --git a/plugin/migrationmoodle/plugin.php b/plugin/migrationmoodle/plugin.php new file mode 100644 index 000000000..f4f3f73a2 --- /dev/null +++ b/plugin/migrationmoodle/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/migrationmoodle/run_cli.php b/plugin/migrationmoodle/run_cli.php new file mode 100644 index 000000000..c6080d6b2 --- /dev/null +++ b/plugin/migrationmoodle/run_cli.php @@ -0,0 +1,120 @@ + $taskName) { + $taskClass = api_underscore_to_camel_case($taskName).'Task'; + $taskClass = 'Chamilo\\PluginBundle\\MigrationMoodle\\Task\\'.$taskClass; + + echo PHP_EOL.'['.date(DateTime::ATOM).'] '.($i + 1).': '; + + if ($plugin->isTaskDone($taskName)) { + echo "Already done \"$taskClass\"".PHP_EOL; + continue; + } + + echo "Executing \"$taskClass.\"".PHP_EOL; + + /** @var BaseTask $task */ + $task = new $taskClass(); + $task->execute(); + + echo '['.date(DateTime::ATOM)."] End \"$taskClass\"".PHP_EOL; +} + +$scriptNames = [ + 'user_learn_paths_progress', + 'user_scorms_progress', +]; + +foreach ($scriptNames as $i => $scriptName) { + $scriptClass = api_underscore_to_camel_case($scriptName).'Script'; + $scriptClass = 'Chamilo\\PluginBundle\\MigrationMoodle\\Script\\'.$scriptClass; + + echo PHP_EOL.'['.date(DateTime::ATOM).'] '.($i + 1).': '; + + if ($plugin->isTaskDone($scriptName)) { + echo "Already done \"$scriptClass\"".PHP_EOL; + continue; + } + + echo "Executing \"$scriptClass.\"".PHP_EOL; + + /** @var BaseScript $script */ + $script = new $scriptClass(); + $script->run(); + + echo '['.date(DateTime::ATOM)."] End \"$scriptClass\"".PHP_EOL; +} diff --git a/plugin/migrationmoodle/src/Extractor/BaseExtractor.php b/plugin/migrationmoodle/src/Extractor/BaseExtractor.php new file mode 100644 index 000000000..12e085956 --- /dev/null +++ b/plugin/migrationmoodle/src/Extractor/BaseExtractor.php @@ -0,0 +1,63 @@ +query = $configuration['query']; + } + + /** + * @return bool + */ + public function filter(array $sourceData) + { + return false; + } + + /** + * @throws \Exception + * + * @return \Generator|iterable + */ + public function extract() + { + $plugin = \MigrationMoodlePlugin::create(); + + try { + $connection = $plugin->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + try { + $statement = $connection->executeQuery($this->query); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"{$this->query}\".", 0, $e); + } + + while ($sourceRow = $statement->fetch(FetchMode::ASSOCIATIVE)) { + yield $sourceRow; + } + + $connection->close(); + } +} diff --git a/plugin/migrationmoodle/src/Extractor/FilterExtractor.php b/plugin/migrationmoodle/src/Extractor/FilterExtractor.php new file mode 100644 index 000000000..604b1d9b6 --- /dev/null +++ b/plugin/migrationmoodle/src/Extractor/FilterExtractor.php @@ -0,0 +1,39 @@ +getTaskName(); + + $result = \Database::select( + 'COUNT(1) AS c', + 'plugin_migrationmoodle_item i INNER JOIN plugin_migrationmoodle_task t ON i.task_id = t.id', + [ + 'where' => [ + 't.name = ? AND i.extracted_id = ?' => [$taskName, $id], + ], + ], + 'first' + ); + + return $result['c'] > 0; + } +} diff --git a/plugin/migrationmoodle/src/Extractor/LoadedCoursesFilterExtractor.php b/plugin/migrationmoodle/src/Extractor/LoadedCoursesFilterExtractor.php new file mode 100644 index 000000000..72a44a133 --- /dev/null +++ b/plugin/migrationmoodle/src/Extractor/LoadedCoursesFilterExtractor.php @@ -0,0 +1,40 @@ +calledClass = CoursesTask::class; + } + + /** + * @return bool + */ + public function filter(array $sourceData) + { + $courseId = $sourceData['id']; + + if (isset($sourceData['course'])) { + $courseId = $sourceData['course']; + } + + return !$this->existsExtracted($courseId); + } +} diff --git a/plugin/migrationmoodle/src/Extractor/LoadedScormsFilterExtractor.php b/plugin/migrationmoodle/src/Extractor/LoadedScormsFilterExtractor.php new file mode 100644 index 000000000..7b0ff2e5c --- /dev/null +++ b/plugin/migrationmoodle/src/Extractor/LoadedScormsFilterExtractor.php @@ -0,0 +1,38 @@ +calledClass = CourseModulesScormTask::class; + } + + /** + * Filter to avoid scorms not yet migrated. + * + * @return bool + */ + public function filter(array $sourceData) + { + $scormId = $sourceData['scorm']; + + return !$this->existsExtracted($scormId); + } +} diff --git a/plugin/migrationmoodle/src/Extractor/LoadedUsersFilterExtractor.php b/plugin/migrationmoodle/src/Extractor/LoadedUsersFilterExtractor.php new file mode 100644 index 000000000..401442287 --- /dev/null +++ b/plugin/migrationmoodle/src/Extractor/LoadedUsersFilterExtractor.php @@ -0,0 +1,38 @@ +calledClass = UsersTask::class; + } + + /** + * @return bool + */ + public function filter(array $sourceData) + { + $userId = $sourceData['id']; + + if (isset($sourceData['userid'])) { + $userId = $sourceData['userid']; + } + + return !$this->existsExtracted($userId); + } +} diff --git a/plugin/migrationmoodle/src/Interfaces/ExtractorInterface.php b/plugin/migrationmoodle/src/Interfaces/ExtractorInterface.php new file mode 100644 index 000000000..30f520fc8 --- /dev/null +++ b/plugin/migrationmoodle/src/Interfaces/ExtractorInterface.php @@ -0,0 +1,22 @@ +updateTitle(\Exercise::format_title_variable($incomingData['exerciseTitle'])); + $exercise->updateDescription($incomingData['exerciseDescription']); + $exercise->updateAttempts($incomingData['exerciseAttempts']); + $exercise->updateFeedbackType(0); + $exercise->updateType(2); + $exercise->setRandom(0); + $exercise->updateRandomAnswers($incomingData['randomAnswers']); + $exercise->updateResultsDisabled(0); + $exercise->updateExpiredTime($incomingData['enabletimercontroltotalminutes']); + //$exercise->updatePropagateNegative($incomingData['propagate_neg']); + //$exercise->updateSaveCorrectAnswers($incomingData['save_correct_answers']); + //$exercise->updateRandomByCat($incomingData['randomByCat']); + $exercise->updateTextWhenFinished(''); + $exercise->updateDisplayCategoryName(1); + //$exercise->updateReviewAnswers($incomingData['review_answers']); + $exercise->updatePassPercentage($incomingData['pass_percentage']); + //$exercise->updateCategories($incomingData['category']); + //$exercise->updateEndButton($incomingData['end_button']); + //$exercise->setOnSuccessMessage($incomingData['on_success_message']); + //$exercise->setOnFailedMessage($incomingData['on_failed_message']); + //$exercise->updateEmailNotificationTemplate($incomingData['email_notification_template']); + //$exercise->setEmailNotificationTemplateToUser($incomingData['email_notification_template_to_user']); + //$exercise->setNotifyUserByEmail($incomingData['notify_user_by_email']); + //$exercise->setModelType($incomingData['model_type']); + $exercise->setQuestionSelectionType(1); + $exercise->setHideQuestionTitle(0); + $exercise->sessionId = 0; + //$exercise->setScoreTypeModel($incomingData['score_type_model']); + //$exercise->setGlobalCategoryId($incomingData['global_category_id']); + //$exercise->setShowPreviousButton($incomingData['show_previous_button']); + //$exercise->setNotifications($incomingData['notifications']); + //$exercise->setExerciseCategoryId($incomingData['exercise_category_id']); + $exercise->setPageResultConfiguration($incomingData); + $exercise->start_time = api_get_utc_datetime($incomingData['start_time'], true); + $exercise->end_time = api_get_utc_datetime($incomingData['end_time'], true); + $exercise->expired_time = $incomingData['enabletimercontroltotalminutes']; + $exercise->random_answers = $incomingData['randomAnswers'] == 1 ? 1 : 0; + + $iId = $exercise->save(); + + return $iId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseCategoriesLoader.php b/plugin/migrationmoodle/src/Loader/CourseCategoriesLoader.php new file mode 100644 index 000000000..b0d556c1e --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseCategoriesLoader.php @@ -0,0 +1,42 @@ +getAccessUrlId(); + + \Database::query("UPDATE $tblUrlCategory SET access_url_id = $accessUrlId WHERE course_category_id = $id"); + + return $id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseFilesLoader.php b/plugin/migrationmoodle/src/Loader/CourseFilesLoader.php new file mode 100644 index 000000000..a6bd2f876 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseFilesLoader.php @@ -0,0 +1,65 @@ +findFilePath($incomingData['contenthash']); + + $file = [ + 'file' => [ + 'name' => $incomingData['filename'], + 'tmp_name' => $filePath, + 'type' => $incomingData['mimetype'], + 'size' => $incomingData['filesize'], + 'error' => 0, + 'from_file' => true, + 'move_file' => true, + ], + ]; + + $_POST['language'] = $courseInfo['language']; + + $fileData = \DocumentManager::upload_document( + $file, + '/', + $incomingData['filename'], + '', + null, + null, + true, + false, + 'file', + false, + $userId, + $courseInfo + ); + + return $fileData['iid']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseIntroductionLoader.php b/plugin/migrationmoodle/src/Loader/CourseIntroductionLoader.php new file mode 100644 index 000000000..591c6dec6 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseIntroductionLoader.php @@ -0,0 +1,30 @@ + 0, + 'c_id' => $incomingData['c_id'], + 'id' => TOOL_COURSE_HOMEPAGE, + 'intro_text' => $incomingData['description'], + ] + ); + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseModulesLessonLoader.php b/plugin/migrationmoodle/src/Loader/CourseModulesLessonLoader.php new file mode 100644 index 000000000..616c65d1b --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseModulesLessonLoader.php @@ -0,0 +1,40 @@ +add_item( + 0, + 0, + 'dir', + 0, + $incomingData['title'], + '' + ); + + return $itemId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseModulesQuizLoader.php b/plugin/migrationmoodle/src/Loader/CourseModulesQuizLoader.php new file mode 100644 index 000000000..9e04f4e49 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseModulesQuizLoader.php @@ -0,0 +1,39 @@ +add_item( + 0, + 0, + 'quiz', + 0, + $incomingData['title'], + '' + ); + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseModulesScormLoader.php b/plugin/migrationmoodle/src/Loader/CourseModulesScormLoader.php new file mode 100644 index 000000000..29e9499ad --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseModulesScormLoader.php @@ -0,0 +1,123 @@ + 0 THEN MAX(display_order) + 1 ELSE 1 END AS display_order + FROM $tblLpMain WHERE c_id = {$incomingData['c_id']}" + ); + $row = \Database::fetch_assoc($resultDisplayOrder); + $displayOrder = $row['display_order']; + + $courseInfo = api_get_course_info_by_id($incomingData['c_id']); + $userId = 1; + + $incomingData['path'] = $this->createDirectory($incomingData['name'], $courseInfo['code']); + $incomingData['use_max_score'] = $incomingData['use_max_score'] == 100; + + $incomingData['created_on'] = $incomingData['created_on'] + ? $incomingData['created_on']->format('Y-m-d h:i:s') + : null; + $incomingData['modified_on'] = $incomingData['modified_on'] + ? $incomingData['modified_on']->format('Y-m-d h:i:s') + : null; + $incomingData['publicated_on'] = $incomingData['publicated_on'] + ? $incomingData['publicated_on']->format('Y-m-d h:i:s') + : null; + + $params = array_merge( + $incomingData, + [ + 'lp_type' => 2, + 'description' => '', + 'force_commit' => 0, + 'default_view_mod' => 'embedded', + 'default_encoding' => 'UTF-8', + 'js_lib' => 'scorm_api.php', + 'display_order' => $displayOrder, + 'session_id' => 0, + 'content_maker' => '', + 'content_license' => '', + 'debug' => 0, + 'theme' => '', + 'preview_image' => '', + 'author' => '', + 'prerequisite' => 0, + 'seriousgame_mode' => 0, + 'autolaunch' => 0, + 'category_id' => 0, + 'max_attempts' => 0, + 'subscribe_users' => 0, + ] + ); + + $lpId = \Database::insert($tblLpMain, $params); + + if ($lpId) { + \Database::query("UPDATE $tblLpMain SET id = iid WHERE iid = $lpId"); + + api_item_property_update($courseInfo, TOOL_LEARNPATH, $lpId, 'LearnpathAdded', $userId); + api_item_property_update($courseInfo, TOOL_LEARNPATH, $lpId, 'visible', $userId); + } + + return $lpId; + } + + /** + * @param string $fileName + * + * @return string + */ + public static function generateDirectoryName($fileName) + { + $newDirectory = trim($fileName); + $newDirectory = trim($newDirectory, '/'); + + return api_replace_dangerous_char($newDirectory); + } + + /** + * @param string $name + * @param string $courseCode + * + * @return string + */ + private function createDirectory($name, $courseCode) + { + $courseRelDir = api_get_path(SYS_COURSE_PATH).api_get_course_path($courseCode).'/scorm'; + + $newDirectory = self::generateDirectoryName($name); + + $fullPath = "$courseRelDir/$newDirectory"; + + $fileSystem = new Filesystem(); + + if (!is_dir($fullPath)) { + $fileSystem->mkdir( + $fullPath, + api_get_permissions_for_new_directories() + ); + } + + return "$newDirectory/."; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseModulesUrlLoader.php b/plugin/migrationmoodle/src/Loader/CourseModulesUrlLoader.php new file mode 100644 index 000000000..e16620071 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseModulesUrlLoader.php @@ -0,0 +1,35 @@ +add_item( + 0, + 0, + 'link', + 0, + $incomingData['title'], + '' + ); + } +} diff --git a/plugin/migrationmoodle/src/Loader/CourseSectionsLoader.php b/plugin/migrationmoodle/src/Loader/CourseSectionsLoader.php new file mode 100644 index 000000000..272e32a2e --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CourseSectionsLoader.php @@ -0,0 +1,52 @@ +generate_lp_folder($courseInfo); + + $itemTitle = get_lang('Description'); + + $documentId = $lp->create_document($courseInfo, $incomingData['description'], $itemTitle); + $lp->add_item(0, 0, 'document', $documentId, $itemTitle, ''); + } + + return $lpId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/CoursesLoader.php b/plugin/migrationmoodle/src/Loader/CoursesLoader.php new file mode 100644 index 000000000..346f371d5 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/CoursesLoader.php @@ -0,0 +1,50 @@ +loadMode === self::LOAD_MODE_REUSE) { + return $courseInfo['real_id']; + } + + if ($this->loadMode === self::LOAD_MODE_DUPLICATE) { + $incomingData['wanted_code'] = $incomingData['wanted_code'].substr(md5(uniqid(rand())), 0, 10); + } + } + + $incomingData['subscribe'] = false; + $incomingData['unsubscribe'] = false; + $incomingData['disk_quota'] = 500 * 1024 * 1024; + + $accessUrlId = \MigrationMoodlePlugin::create()->getAccessUrlId(); + + $courseInfo = \CourseManager::create_course($incomingData, 1, $accessUrlId); + + return $courseInfo['real_id']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/FilesForScormScoLoader.php b/plugin/migrationmoodle/src/Loader/FilesForScormScoLoader.php new file mode 100644 index 000000000..1fbc5bf75 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/FilesForScormScoLoader.php @@ -0,0 +1,46 @@ +findFilePath($incomingData['contenthash']); + + $sysCourseScormPath = api_get_path(SYS_COURSE_PATH).$course->getDirectory().'/scorm'; + $lpDirectory = CourseModulesScormLoader::generateDirectoryName($incomingData['lp_name']); + $lpDirectoryPath = "$sysCourseScormPath/$lpDirectory"; + $fileDirectoryPath = $lpDirectoryPath.$incomingData['filepath']; + $filePath = $fileDirectoryPath.$incomingData['filename']; + + $fileSystem = new Filesystem(); + + if ($incomingData['filepath'] != '/') { + $fileSystem->mkdir( + $fileDirectoryPath, + api_get_permissions_for_new_directories() + ); + } + + $fileSystem->copy($moodleFilePath, $filePath); + + return 0; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonAnswersEssayLoader.php b/plugin/migrationmoodle/src/Loader/LessonAnswersEssayLoader.php new file mode 100644 index 000000000..66900f8c9 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonAnswersEssayLoader.php @@ -0,0 +1,36 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + + $question->weighting = $incomingData['score']; + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonAnswersMatchingLoader.php b/plugin/migrationmoodle/src/Loader/LessonAnswersMatchingLoader.php new file mode 100644 index 000000000..047aa7fe9 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonAnswersMatchingLoader.php @@ -0,0 +1,68 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + + $answer = new \Answer($incomingData['question_id'], $incomingData['c_id'], $exercise); + $questionsAnswers = $answer->getAnswers(); + + foreach ($questionsAnswers as $questionsAnswer) { + $answer->createAnswer( + $questionsAnswer['answer'], + $questionsAnswer['correct'], + $questionsAnswer['comment'], + $questionsAnswer['ponderation'], + $questionsAnswer['position'], + $questionsAnswer['hotspot_coordinates'], + $questionsAnswer['hotspot_type'], + $questionsAnswer['destination'] + ); + } + + $optionPosition = $question->countAnswers() + 1; + + $answer->createAnswer($incomingData['feedback'], 0, '', 0, $optionPosition); + + $answerPosition = $optionPosition + 1; + + $answer->createAnswer( + $incomingData['answer'], + $optionPosition, + '', + $incomingData['score'], + $answerPosition + ); + + $answer->save(); + + $question->weighting += $incomingData['score']; + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleAnswerLoader.php b/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleAnswerLoader.php new file mode 100644 index 000000000..a92ce1da8 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleAnswerLoader.php @@ -0,0 +1,72 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + + $answer = new \Answer($incomingData['question_id'], $incomingData['c_id'], $exercise); + $questionsAnswers = $answer->getAnswers(); + + foreach ($questionsAnswers as $questionsAnswer) { + $answer->createAnswer( + $questionsAnswer['answer'], + $questionsAnswer['correct'], + $questionsAnswer['comment'], + $questionsAnswer['ponderation'], + $questionsAnswer['position'], + $questionsAnswer['hotspot_coordinates'], + $questionsAnswer['hotspot_type'], + $questionsAnswer['destination'] + ); + } + + $incomingData['score'] = abs($incomingData['score']); + + if (!$incomingData['is_correct']) { + $incomingData['score'] = -$incomingData['score']; + } + + if ($incomingData['score'] > 0) { + $question->weighting += $incomingData['score']; + } + + $answer->createAnswer( + $incomingData['answer'], + $incomingData['is_correct'], + $incomingData['feedback'], + $incomingData['score'], + $question->countAnswers() + 1, + null, + null, + CQuizAnswer::DEFAULT_DESTINATION + ); + + $answer->save(); + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleChoiceLoader.php b/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleChoiceLoader.php new file mode 100644 index 000000000..9e2a1696b --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonAnswersMultipleChoiceLoader.php @@ -0,0 +1,22 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + $question->weighting = 0; + + $answer = new \Answer($incomingData['question_id'], $incomingData['c_id'], $exercise); + + $incomingData['answers'] = '['.$incomingData['answers'].']'; + // Remove the "::" eventually written by the user + $incomingData['answers'] = str_replace('::', '', $incomingData['answers']); + // Remove starting and ending space and   + $incomingData['answers'] = api_preg_replace("/\xc2\xa0/", " ", $incomingData['answers']); + + $blankStartSeparator = '['; + $blankEndSeparator = ']'; + $blankStartSeparatorRegexp = \FillBlanks::escapeForRegexp($blankStartSeparator); + $blankEndSeparatorRegexp = \FillBlanks::escapeForRegexp($blankEndSeparator); + + // Remove spaces at the beginning and the end of text in square brackets + $return = preg_replace_callback( + "/".$blankStartSeparatorRegexp."[^]]+".$blankEndSeparatorRegexp."/", + function ($matches) use ($blankStartSeparator, $blankEndSeparator) { + $matchingResult = $matches[0]; + $matchingResult = trim($matchingResult, $blankStartSeparator); + $matchingResult = trim($matchingResult, $blankEndSeparator); + $matchingResult = trim($matchingResult); + // Remove forbidden chars + $matchingResult = str_replace("/\\/", "", $matchingResult); + $matchingResult = str_replace('/"/', "", $matchingResult); + + return $blankStartSeparator.$matchingResult.$blankEndSeparator; + }, + $incomingData['answers'] + ); + + $question->weighting += $incomingData['scores']; + + $return .= '::'.$incomingData['scores'].':'; + $return .= self::INPUT_WIDTH; + $return .= ':0@'; + + $answer->createAnswer($return, 0, $incomingData['comment'], 0, 1); + $answer->save(); + + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonAnswersTrueFalseLoader.php b/plugin/migrationmoodle/src/Loader/LessonAnswersTrueFalseLoader.php new file mode 100644 index 000000000..ae4f35160 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonAnswersTrueFalseLoader.php @@ -0,0 +1,70 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + + $answer = new \Answer($incomingData['question_id'], $incomingData['c_id'], $exercise); + $questionsAnswers = $answer->getAnswers(); + + foreach ($questionsAnswers as $questionsAnswer) { + $answer->createAnswer( + $questionsAnswer['answer'], + $questionsAnswer['correct'], + $questionsAnswer['comment'], + $questionsAnswer['ponderation'], + $questionsAnswer['position'], + $questionsAnswer['hotspot_coordinates'], + $questionsAnswer['hotspot_type'], + $questionsAnswer['destination'] + ); + } + + if ($incomingData['is_correct']) { + $incomingData['score'] = abs($incomingData['score']); + + $question->weighting += $incomingData['score']; + } + + $answer->createAnswer( + $incomingData['answer'], + $incomingData['is_correct'], + $incomingData['feedback'], + $incomingData['score'], + $question->countAnswers() + 1, + null, + null, + CQuizAnswer::DEFAULT_DESTINATION + ); + + $answer->save(); + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonPagesDocumentLoader.php b/plugin/migrationmoodle/src/Loader/LessonPagesDocumentLoader.php new file mode 100644 index 000000000..5e89f9511 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonPagesDocumentLoader.php @@ -0,0 +1,47 @@ +generate_lp_folder($courseInfo); + + $docId = $lp->create_document( + $courseInfo, + $incomingData['item_content'], + $incomingData['item_title'], + 'html' + ); + + $tblLpItem = \Database::get_course_table(TABLE_LP_ITEM); + + \Database::query("UPDATE $tblLpItem SET path = '$docId' WHERE iid = {$incomingData['item_id']}"); + + return $docId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonPagesLoader.php b/plugin/migrationmoodle/src/Loader/LessonPagesLoader.php new file mode 100644 index 000000000..d0f23181a --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonPagesLoader.php @@ -0,0 +1,40 @@ +add_item( + $incomingData['parent'], + $incomingData['previous'], + $incomingData['item_type'], + 0, + $incomingData['title'], + '' + ); + + return $itemId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuestionLoader.php b/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuestionLoader.php new file mode 100644 index 000000000..f0e1441ce --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuestionLoader.php @@ -0,0 +1,40 @@ +read($incomingData['quiz_id']); + + $question = \Question::getInstance($incomingData['question_type']); + $question->course = api_get_course_info_by_id($incomingData['c_id']); + $question->updateTitle($incomingData['question_title']); + $question->updateLevel(1); + $question->updateCategory(0); + $question->save($exercise); + + $exercise->addToList($question->id); + $exercise->update_question_positions(); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuizLoader.php b/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuizLoader.php new file mode 100644 index 000000000..ee4830e09 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/LessonQuestionPagesQuizLoader.php @@ -0,0 +1,55 @@ +updateTitle(\Exercise::format_title_variable($incomingData['item_title'])); + $exercise->updateDescription(''); + $exercise->updateAttempts(0); + $exercise->updateFeedbackType(0); + $exercise->updateType(ALL_ON_ONE_PAGE); + $exercise->setRandom(0); + $exercise->updateRandomAnswers(0); + $exercise->updateResultsDisabled(0); + $exercise->updateExpiredTime(0); + $exercise->updateTextWhenFinished(''); + $exercise->updateDisplayCategoryName(1); + $exercise->updatePassPercentage(0); + $exercise->setQuestionSelectionType(1); + $exercise->setHideQuestionTitle(0); + $exercise->sessionId = 0; + $exercise->start_time = null; + $exercise->end_time = null; + $exercise->active = false; + + $quizId = $exercise->save(); + + $tblLpItem = \Database::get_course_table(TABLE_LP_ITEM); + + \Database::query("UPDATE $tblLpItem SET path = '$quizId' WHERE iid = {$incomingData['item_id']}"); + + return $quizId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/QuestionCategoriesLoader.php b/plugin/migrationmoodle/src/Loader/QuestionCategoriesLoader.php new file mode 100644 index 000000000..4b2fb4f7c --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/QuestionCategoriesLoader.php @@ -0,0 +1,44 @@ +name = $incomingData['name']; + $category->description = $incomingData['description']; + + $id = $category->save($incomingData['c_id']); + + if (false === $id) { + throw new \Exception("The quiz category \"{$incomingData['name']}\" already exists."); + } + + return $id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/QuestionGapselectLoader.php b/plugin/migrationmoodle/src/Loader/QuestionGapselectLoader.php new file mode 100644 index 000000000..8d54f0a56 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/QuestionGapselectLoader.php @@ -0,0 +1,37 @@ +read($incomingData['quiz_id']); + + $question = \Question::read($incomingData['question_id'], $courseInfo); + $question->setTitle(get_lang('FillBlanks')); + $question->weighting = $incomingData['score']; + + $answer = new \Answer($incomingData['question_id'], $incomingData['c_id'], $exercise); + $answer->createAnswer($incomingData['answer'], 0, $incomingData['comment'], 0, 1); + $answer->save(); + + $question->save($exercise); + + return $question->id; + } +} diff --git a/plugin/migrationmoodle/src/Loader/QuizzesLoader.php b/plugin/migrationmoodle/src/Loader/QuizzesLoader.php new file mode 100644 index 000000000..e9c97e569 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/QuizzesLoader.php @@ -0,0 +1,50 @@ +updateTitle(\Exercise::format_title_variable($incomingData['exerciseTitle'])); + $exercise->updateDescription($incomingData['exerciseDescription']); + $exercise->updateAttempts($incomingData['exerciseAttempts']); + $exercise->updateFeedbackType(0); + $exercise->updateType(ALL_ON_ONE_PAGE); + $exercise->setRandom(0); + $exercise->updateRandomAnswers($incomingData['randomAnswers']); + $exercise->updateResultsDisabled(0); + $exercise->updateExpiredTime($incomingData['enabletimercontroltotalminutes']); + $exercise->updateTextWhenFinished(''); + $exercise->updateDisplayCategoryName(1); + $exercise->updatePassPercentage($incomingData['pass_percentage']); + $exercise->setQuestionSelectionType(1); + $exercise->setHideQuestionTitle(0); + $exercise->sessionId = 0; + $exercise->start_time = api_get_utc_datetime($incomingData['start_time'], true); + $exercise->end_time = api_get_utc_datetime($incomingData['end_time'], true); + + $quizId = $exercise->save(); + + \Database::query("UPDATE c_quiz SET active = 0 WHERE iid = $quizId"); + \Database::query("UPDATE c_lp_item SET path = '$quizId' WHERE iid = {$incomingData['item_id']}"); + + return $quizId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/QuizzesScoresLoader.php b/plugin/migrationmoodle/src/Loader/QuizzesScoresLoader.php new file mode 100644 index 000000000..3ea04dc20 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/QuizzesScoresLoader.php @@ -0,0 +1,39 @@ +add_item( + $incomingData['parent_item_id'], + 0, + $incomingData['item_type'], + 0, + $incomingData['title'], + '' + ); + + $tblLpItem = \Database::get_course_table(TABLE_LP_ITEM); + + \Database::query( + "UPDATE $tblLpItem SET path = '{$incomingData['path']}', ref = '{$incomingData['ref']}' WHERE iid = $itemId" + ); + + return $itemId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/SortSectionModuleLoader.php b/plugin/migrationmoodle/src/Loader/SortSectionModuleLoader.php new file mode 100644 index 000000000..60f49254f --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/SortSectionModuleLoader.php @@ -0,0 +1,51 @@ + [ + 'c_id = ? AND lp_id = ? AND parent_item_id = ? AND item_type = ?' => [ + $incomingData['c_id'], + $incomingData['lp_id'], + 0, + TOOL_DOCUMENT, + ], + ], + 'order' => 'iid ASC', + ], + 'first' + ); + + $orderList = $incomingData['order_list']; + + if ($firstItem) { + $orderList = [$firstItem['iid'] => 0] + $orderList; + } + + \learnpath::sortItemByOrderList( + $orderList, + $incomingData['c_id'] + ); + + return $incomingData['lp_id']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/TrackCourseAccessLoader.php b/plugin/migrationmoodle/src/Loader/TrackCourseAccessLoader.php new file mode 100644 index 000000000..37d25c85e --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/TrackCourseAccessLoader.php @@ -0,0 +1,66 @@ +modify("-$sessionLifetime seconds"); + + $time = $time->format('Y-m-d H:i:s'); + $loginCourseDate = $loginCourseDate->format('Y-m-d H:i:s'); + + $tableCourseAccess = \Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS); + + $result = \Database::query( + "SELECT course_access_id + FROM $tableCourseAccess + WHERE user_id = $userId AND c_id = $cId AND session_id = $sessionId AND login_course_date > '$time' + ORDER BY login_course_date DESC + LIMIT 1" + ); + + if (\Database::num_rows($result) > 0) { + $row = \Database::fetch_assoc($result); + + \Database::query( + "UPDATE $tableCourseAccess + SET logout_course_date = '$loginCourseDate', counter = counter + 1 + WHERE course_access_id = {$row['course_access_id']}" + ); + + return $row['course_access_id']; + } + + return \Database::insert( + $tableCourseAccess, + [ + 'c_id' => $cId, + 'user_ip' => $ip, + 'user_id' => $userId, + 'login_course_date' => $loginCourseDate, + 'logout_course_date' => $loginCourseDate, + 'counter' => 1, + 'session_id' => $sessionId, + ] + ); + } +} diff --git a/plugin/migrationmoodle/src/Loader/TrackLoginLoader.php b/plugin/migrationmoodle/src/Loader/TrackLoginLoader.php new file mode 100644 index 000000000..f858c03f2 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/TrackLoginLoader.php @@ -0,0 +1,57 @@ +format('Y-m-d H:i:s'); + + if ($incomingData['logout_date']) { + $incomingData['logout_date'] = $incomingData['logout_date']->format('Y-m-d H:i:s'); + } + + $tblTrackELogin = \Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN); + + $firstId = \Database::insert( + $tblTrackELogin, + [ + 'login_user_id' => $incomingData['login_user_id'], + 'login_date' => $incomingData['login_date'], + 'user_ip' => '', + ] + ); + + \Database::update( + $tblTrackELogin, + ['logout_date' => $incomingData['logout_date']], + ['login_id = ?' => [$firstId]] + ); + + $incomingData['user_ip'] = ''; + + \Database::insert( + $tblTrackELogin, + [ + 'login_user_id' => $incomingData['login_user_id'], + 'login_date' => $incomingData['logout_date'], + 'logout_date' => $incomingData['logout_date'], + 'user_ip' => '', + ] + ); + + return $incomingData['login_user_id']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UrlLoader.php b/plugin/migrationmoodle/src/Loader/UrlLoader.php new file mode 100644 index 000000000..3b18698ad --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UrlLoader.php @@ -0,0 +1,45 @@ + $incomingData['c_id'], + 'url' => $incomingData['url'], + 'title' => $incomingData['title'], + 'description' => null, + 'category_id' => null, + 'on_homepage' => '0', + 'target' => '_self', + 'session_id' => 0, + ]; + + $link = new \Link(); + $link->setCourse( + api_get_course_info_by_id($incomingData['c_id']) + ); + $linkId = $link->save($params); + + $tblLpItem = \Database::get_course_table(TABLE_LP_ITEM); + + \Database::query("UPDATE $tblLpItem SET path = '$linkId' WHERE iid = {$incomingData['item_id']}"); + + return $linkId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserLastLoginLoader.php b/plugin/migrationmoodle/src/Loader/UserLastLoginLoader.php new file mode 100644 index 000000000..fda4317db --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserLastLoginLoader.php @@ -0,0 +1,28 @@ + $incomingData['last_login']->format('Y-m-d H:i:s')], + ['id = ?' => [$incomingData['user_id']]] + ); + + return $incomingData['user_id']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserLearnPathLessonAttemptLoader.php b/plugin/migrationmoodle/src/Loader/UserLearnPathLessonAttemptLoader.php new file mode 100644 index 000000000..45cb76598 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserLearnPathLessonAttemptLoader.php @@ -0,0 +1,32 @@ +findViewOfItem($incomingData); + + $itemViewParams = ['status' => 'completed']; + + if ($item['display_order'] != 1) { + $previousItemView = $this->findViewOfPreviousItem($incomingData); + + $itemViewParams['start_time'] = $previousItemView['start_time'] + $previousItemView['total_time']; + $itemView['start_time'] = $itemViewParams['start_time']; + } + + $itemViewParams['total_time'] = $incomingData['end_time'] - $itemView['start_time']; + + \Database::update( + $tblLpItemView, + $itemViewParams, + ['iid = ?' => [$itemView['iid']]] + ); + + return $itemView['iid']; + } + + /** + * @throws \Exception + * + * @return array + */ + private function findViewOfItem(array $incomingData) + { + $itemView = \Database::fetch_assoc( + \Database::query( + "SELECT lpiv.iid, lpiv.start_time + FROM c_lp_item_view lpiv + INNER JOIN c_lp_view lpv ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + WHERE lpiv.lp_item_id = {$incomingData['item_id']} AND lpv.user_id = {$incomingData['user_id']} + LIMIT 1" + ) + ); + + if (!$itemView) { + throw new \Exception("Item view not found for "."item ({$incomingData['item_id']}) and user ({$incomingData['user_id']})."); + } + + return $itemView; + } + + /** + * @throws \Exception + * + * @return array + */ + private function findViewOfPreviousItem(array $incomingData) + { + $result = \Database::query( + "SELECT lpiv.start_time, lpiv.total_time + FROM c_lp_item_view lpiv + INNER JOIN c_lp_view lpv ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + INNER JOIN c_lp_item lpi ON (lpi.iid = lpiv.lp_item_id AND lpi.c_id = lpiv.c_id) + WHERE lpi.next_item_id = {$incomingData['item_id']} AND lpv.user_id = {$incomingData['user_id']} + LIMIT 1" + ); + $previousItemView = \Database::fetch_assoc($result); + + if (!$previousItemView) { + throw new \Exception("Item view not found for "."previous item ({$incomingData['item_id']}) and user ({$incomingData['user_id']})."); + } + + return $previousItemView; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserLearnPathLessonTimerLoader.php b/plugin/migrationmoodle/src/Loader/UserLearnPathLessonTimerLoader.php new file mode 100644 index 000000000..053b5e1bf --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserLearnPathLessonTimerLoader.php @@ -0,0 +1,90 @@ +findViewOfParentItem($incomingData); + $itemView = $this->findViewOfFirstItem($incomingData); + + \Database::query( + "UPDATE $tblItemView SET start_time = {$incomingData['start_time']} WHERE iid = {$parentItemView['iid']}" + ); + \Database::query( + "UPDATE $tblItemView SET start_time = {$incomingData['start_time']} WHERE iid = {$itemView['iid']}" + ); + + return $itemView['iid']; + } + + /** + * @throws \Exception + * + * @return array + */ + private function findViewOfParentItem(array $incomingData) + { + $parentItemView = \Database::fetch_assoc( + \Database::query( + "SELECT lpiv.iid + FROM c_lp_item_view lpiv + INNER JOIN c_lp_view lpv ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + WHERE lpiv.lp_item_id = {$incomingData['parent_item_id']} + AND lpv.user_id = {$incomingData['user_id']} + LIMIT 1" + ) + ); + + if (!$parentItemView) { + throw new \Exception("Item dir ({$incomingData['parent_item_id']}) not found."); + } + + return $parentItemView; + } + + /** + * @throws \Exception + * + * @return array + */ + private function findViewOfFirstItem(array $incomingData) + { + $itemView = \Database::fetch_assoc( + \Database::query( + "SELECT lpiv.iid + FROM c_lp_item_view lpiv + INNER JOIN c_lp_view lpv + ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + INNER JOIN c_lp_item lpi + ON (lpi.lp_id = lpv.lp_id AND lpi.c_id = lpv.c_id AND lpi.iid = lpiv.lp_item_id) + WHERE lpi.item_type = 'document' + AND lpv.user_id = {$incomingData['user_id']} + AND lpi.parent_item_id = {$incomingData['parent_item_id']} + AND lpv.session_id = {$incomingData['session_id']} + ORDER BY lpi.display_order ASC + LIMIT 1" + ) + ); + + if (!$itemView) { + throw new \Exception("Item view not found for item with"." parent item ({$incomingData['parent_item_id']}) and user ({$incomingData['user_id']})"); + } + + return $itemView; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserLearnPathQuizLoader.php b/plugin/migrationmoodle/src/Loader/UserLearnPathQuizLoader.php new file mode 100644 index 000000000..dde61fbdd --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserLearnPathQuizLoader.php @@ -0,0 +1,66 @@ +findViewOfItem( + $incomingData['item_id'], + $incomingData['user_id'], + $incomingData['session_id'] + ); + + \Database::update( + \Database::get_course_table(TABLE_LP_ITEM_VIEW), + [ + 'start_time' => $incomingData['start_time'], + 'total_time' => $incomingData['total_time'], + 'score' => $incomingData['score'], + 'status' => $incomingData['status'], + ], + ['iid = ?' => $itemView['iid']] + ); + + return $itemView['iid']; + } + + /** + * @param int $itemId + * @param int $userId + * @param int $sessionId + * + * @throws \Exception + * + * @return array + */ + private function findViewOfItem($itemId, $userId, $sessionId) + { + $result = \Database::query( + "SELECT lpiv.iid + FROM c_lp_item_view lpiv + INNER JOIN c_lp_view lpv ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + WHERE lpiv.lp_item_id = $itemId AND lpv.user_id = $userId AND lpv.session_id = $sessionId + LIMIT 1" + ); + $itemView = \Database::fetch_assoc($result); + + if (!$itemView) { + throw new \Exception("Item view not found for item ($itemId) and user ($userId) in session ($sessionId)."); + } + + return $itemView; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserLearnPathsLoader.php b/plugin/migrationmoodle/src/Loader/UserLearnPathsLoader.php new file mode 100644 index 000000000..8922725c1 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserLearnPathsLoader.php @@ -0,0 +1,73 @@ + $subscription['c_id'], + 'lp_id' => $lp['iid'], + 'user_id' => $incomingData['user_id'], + 'view_count' => 1, + 'session_id' => $subscription['session_id'], + 'last_item' => 0, + ] + ); + \Database::query("UPDATE $tblLpView SET id = iid WHERE iid = $lpViewId"); + + $resItems = \Database::query( + "SELECT iid, max_score FROM $tblLpItem + WHERE lp_id = {$lp['iid']} ORDER BY parent_item_id ASC, display_order ASC" + ); + while ($lpItem = \Database::fetch_assoc($resItems)) { + $lpItemViewId = \Database::insert( + $tblLpItemView, + [ + 'c_id' => $subscription['c_id'], + 'lp_item_id' => $lpItem['iid'], + 'lp_view_id' => $lpViewId, + 'view_count' => 1, + 'status' => 'not attempted', + 'start_time' => 0, + 'total_time' => 0, + 'score' => 0, + 'max_score' => $lpItem['max_score'], + ] + ); + \Database::query("UPDATE $tblLpItemView SET id = iid WHERE iid = $lpItemViewId"); + } + } + } + + return $incomingData['user_id']; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserQuestionAttemptLoader.php b/plugin/migrationmoodle/src/Loader/UserQuestionAttemptLoader.php new file mode 100644 index 000000000..0397ec73c --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserQuestionAttemptLoader.php @@ -0,0 +1,30 @@ +format('Y-m-d H:i:s'); + $incomingData['position'] = 0; + + return \Database::insert( + \Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT), + $incomingData + ); + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserQuizAttemptLoader.php b/plugin/migrationmoodle/src/Loader/UserQuizAttemptLoader.php new file mode 100644 index 000000000..14c98082e --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserQuizAttemptLoader.php @@ -0,0 +1,80 @@ +findViewByQuiz( + $incomingData['exo_id'], + $incomingData['user_id'], + $incomingData['session_id'] + ); + + /** @var \DateTime $exeDate */ + $exeDate = clone $incomingData['date']; + $exeDate->modify("+{$incomingData['duration']} seconds"); + + return \Database::insert( + \Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES), + [ + 'exe_exo_id' => $incomingData['exo_id'], + 'exe_user_id' => $incomingData['user_id'], + 'c_id' => $view['c_id'], + 'status' => $incomingData['status'] === 'finished' ? '' : 'incomplete', + 'session_id' => $incomingData['session_id'], + 'data_tracking' => $incomingData['data_tracking'], + 'start_date' => $incomingData['date']->format('Y-m-d H:i:s'), + 'orig_lp_id' => $view['lp_id'], + 'orig_lp_item_id' => $view['lp_item_id'], + 'orig_lp_item_view_id' => $view['iid'], + 'exe_weighting' => $incomingData['weighting'], + 'user_ip' => '', + 'exe_date' => $exeDate->format('Y-m-d H:i:s'), + 'exe_result' => (float) $incomingData['result'], + 'steps_counter' => 0, + 'exe_duration' => $incomingData['duration'], + 'questions_to_check' => '', + ] + ); + } + + /** + * @param int $quizId + * @param int $userId + * @param int $sessionId + * + * @throws \Exception + * + * @return array + */ + private function findViewByQuiz($quizId, $userId, $sessionId) + { + $query = \Database::query("SELECT lpiv.lp_item_id, lpv.c_id, lpiv.iid, lpv.lp_id + FROM c_lp_item_view lpiv + INNER JOIN c_lp_item lpi ON (lpiv.lp_item_id = lpi.iid AND lpiv.c_id = lpi.c_id) + INNER JOIN c_lp_view lpv ON (lpv.iid = lpiv.lp_view_id AND lpv.c_id = lpiv.c_id) + WHERE lpi.path = $quizId AND lpv.user_id = $userId AND lpv.session_id = $sessionId + LIMIT 1" + ); + $result = \Database::fetch_assoc($query); + + if (!$result) { + throw new \Exception("Item view not found for quiz ($quizId) and user ($userId) in session ($sessionId)."); + } + + return $result; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UserSessionLoader.php b/plugin/migrationmoodle/src/Loader/UserSessionLoader.php new file mode 100644 index 000000000..d617d136d --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UserSessionLoader.php @@ -0,0 +1,79 @@ +loadMode == self::LOAD_MODE_REUSE) { + return $sessionInfo['id']; + } + + if ($this->loadMode === self::LOAD_MODE_DUPLICATE) { + $incomingData['name'] = '['.substr(md5(uniqid(rand())), 0, 5).'] '.$incomingData['name']; + } + } + + $urlId = \MigrationMoodlePlugin::create()->getAccessUrlId(); + $datetime = api_get_utc_datetime(); + $coachId = 1; + + $sessionId = \SessionManager::create_session( + $incomingData['name'], + $datetime, + '', + $datetime, + '', + $datetime, + '', + $coachId, + 0, + 1, + false, + null, + null, + 0, + [], + 0, + false, + $urlId + ); + \SessionManager::add_courses_to_session($sessionId, $incomingData['course_ids']); + \SessionManager::subscribeUsersToSession($sessionId, [$incomingData['user_id']]); + + return $sessionId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UsersLoader.php b/plugin/migrationmoodle/src/Loader/UsersLoader.php new file mode 100644 index 000000000..9bcf108e6 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UsersLoader.php @@ -0,0 +1,97 @@ +loadMode == self::LOAD_MODE_REUSE) { + return $userInfo['id']; + } + + if ($this->loadMode === self::LOAD_MODE_DUPLICATE) { + $incomingData['username'] .= substr(md5(uniqid(rand())), 0, 10); + } + } + + $userId = \UserManager::create_user( + $incomingData['firstname'], + $incomingData['lastname'], + $incomingData['status'], + $incomingData['email'], + $incomingData['username'], + md5(time()), + '', + $incomingData['language'], + $incomingData['phone'], + null, + $incomingData['auth_source'], + null, + $incomingData['active'], + 0, + [], + null, + false, + false, + $incomingData['address'], + false, + null, + 0, + [] + ); + + if (empty($userId)) { + throw new \Exception('User was not created'); + } + + if ($incomingData['registration_date']) { + $incomingData['registration_date'] = $incomingData['registration_date']->format('Y-m-d H:i:s'); + + \Database::query( + "UPDATE $tblUser SET registration_date = '{$incomingData['registration_date']}' WHERE id = $userId" + ); + } + + \UserManager::update_extra_field_value($userId, 'moodle_password', $incomingData['plain_password']); + + $urlId = \MigrationMoodlePlugin::create()->getAccessUrlId(); + + if ($urlId) { + \Database::query("UPDATE access_url_rel_user SET access_url_id = $urlId WHERE user_id = $userId"); + } + + return $userId; + } +} diff --git a/plugin/migrationmoodle/src/Loader/UsersScormsViewLoader.php b/plugin/migrationmoodle/src/Loader/UsersScormsViewLoader.php new file mode 100644 index 000000000..995404a16 --- /dev/null +++ b/plugin/migrationmoodle/src/Loader/UsersScormsViewLoader.php @@ -0,0 +1,163 @@ +getUserSubscriptionInSession($incomingData['user_id'], $incomingData['c_id']); + + $lpViewId = $this->getLpView( + $incomingData['user_id'], + $incomingData['lp_id'], + $incomingData['c_id'], + $sessionId + ); + + $lpItemViewId = $this->getLpItemView($lpViewId, $incomingData['lp_item_id']); + + $itemView = [ + 'c_id' => $incomingData['c_id'], + 'lp_item_id' => $incomingData['lp_item_id'], + 'lp_view_id' => $lpViewId, + 'view_count' => $incomingData['lp_item_view_count'], + 'status' => 'not attempted', + 'start_time' => 0, + 'total_time' => 0, + 'score' => 0, + 'max_score' => 100, + ]; + + foreach (array_keys($itemView) as $key) { + if (isset($incomingData['item_data'][$key])) { + $itemView[$key] = $incomingData['item_data'][$key]; + } + } + + if (empty($lpItemViewId)) { + $lpItemViewId = \Database::insert($tblLpItemView, $itemView); + \Database::query("UPDATE $tblLpItemView SET id = iid WHERE iid = $lpItemViewId"); + } else { + \Database::update($tblLpItemView, $itemView, ['iid = ?' => [$lpItemViewId]]); + } + + \Database::query( + "UPDATE $tblLpView + SET last_item = {$incomingData['lp_item_id']}, + view_count = {$incomingData['lp_item_view_count']} + WHERE iid = $lpViewId" + ); + + return $lpViewId; + } + + /** + * @param int $userId + * @param int $courseId + * + * @throws \Exception + * + * @return int + */ + private function getUserSubscriptionInSession($userId, $courseId) + { + $srcru = \Database::select( + 'session_id', + \Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER), + [ + 'where' => [ + 'user_id = ? AND c_id = ?' => [$userId, $courseId], + ], + ], + 'first' + ); + + if (empty($srcru)) { + throw new \Exception("Session not found for user ($userId) with course ($courseId)"); + } + + return $srcru['session_id']; + } + + /** + * @param int $userId + * @param int $lpId + * @param int $cId + * @param int $sessionId + * + * @return int + */ + private function getLpView($userId, $lpId, $cId, $sessionId) + { + $tblLpView = \Database::get_course_table(TABLE_LP_VIEW); + + $lpView = \Database::select( + 'iid', + $tblLpView, + [ + 'where' => [ + 'user_id = ? AND lp_id = ? AND c_id = ? AND session_id = ?' => [ + $userId, + $lpId, + $cId, + $sessionId, + ], + ], + 'order' => 'view_count DESC', + ], + 'first' + ); + + if (empty($lpView)) { + $lpView = [ + 'c_id' => $cId, + 'lp_id' => $lpId, + 'user_id' => $userId, + 'view_count' => 1, + 'session_id' => $sessionId, + 'last_item' => 0, + ]; + + $lpViewId = \Database::insert($tblLpView, $lpView); + \Database::query("UPDATE $tblLpView SET id = iid WHERE iid = $lpViewId"); + + return $lpViewId; + } + + return $lpView['iid']; + } + + /** + * @param int $lpViewId + * @param int $lpItemId + * + * @return int + */ + private function getLpItemView($lpViewId, $lpItemId) + { + $lpItemView = \Database::fetch_assoc( + \Database::query("SELECT iid FROM c_lp_item_view WHERE lp_view_id = $lpViewId AND lp_item_id = $lpItemId") + ); + + if (empty($lpItemView)) { + return 0; + } + + return $lpItemView['iid']; + } +} diff --git a/plugin/migrationmoodle/src/Messages/ExtractMessage.php b/plugin/migrationmoodle/src/Messages/ExtractMessage.php new file mode 100644 index 000000000..b85b12112 --- /dev/null +++ b/plugin/migrationmoodle/src/Messages/ExtractMessage.php @@ -0,0 +1,24 @@ +incomingData = $incomingData; + + parent::__construct($message, $previous); + } + + public function displayAsString() + { + $pieces = [ + parent::displayAsString(), + "\t".print_r($this->incomingData, true), + ]; + + echo implode(PHP_EOL, $pieces); + } +} diff --git a/plugin/migrationmoodle/src/Messages/Message.php b/plugin/migrationmoodle/src/Messages/Message.php new file mode 100644 index 000000000..31edd2bd5 --- /dev/null +++ b/plugin/migrationmoodle/src/Messages/Message.php @@ -0,0 +1,63 @@ +message = $message; + $this->previous = $previous; + + $this->displayAsString(); + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return Throwable + */ + public function getPrevious() + { + return $this->previous; + } + + public function displayAsString() + { + $pieces = [$this->message]; + + if ($this->previous) { + $pieces[] = "\t".$this->previous->getMessage(); + } + + echo implode(PHP_EOL, $pieces); + } +} diff --git a/plugin/migrationmoodle/src/Messages/TransformMessage.php b/plugin/migrationmoodle/src/Messages/TransformMessage.php new file mode 100644 index 000000000..2c5c48dc3 --- /dev/null +++ b/plugin/migrationmoodle/src/Messages/TransformMessage.php @@ -0,0 +1,40 @@ +extractedData = $extractedData; + + parent::__construct($message, $previous); + } + + public function displayAsString() + { + $pieces = [ + parent::displayAsString(), + "\t".print_r($this->extractedData, true), + ]; + + echo implode(PHP_EOL, $pieces); + } +} diff --git a/plugin/migrationmoodle/src/MigrationMoodleCheckLoginCredentialsHook.php b/plugin/migrationmoodle/src/MigrationMoodleCheckLoginCredentialsHook.php new file mode 100644 index 000000000..2184f1bdc --- /dev/null +++ b/plugin/migrationmoodle/src/MigrationMoodleCheckLoginCredentialsHook.php @@ -0,0 +1,82 @@ +getEventData(); + /** @var array $userData */ + $userData = $data['user']; + /** @var array $credentials */ + $credentials = $data['credentials']; + + $extraField = $this->getExtraField(); + + if (empty($extraField)) { + return false; + } + + $fieldValue = $this->getExtraFieldValue($extraField, $userData); + + if (empty($fieldValue)) { + return false; + } + + $isPasswordVerified = password_verify( + $credentials['password'], + $fieldValue->getValue() + ); + + if (!$isPasswordVerified) { + return false; + } + + return true; + } + + /** + * @return ExtraField|null + */ + private function getExtraField() + { + return Database::getManager() + ->getRepository('ChamiloCoreBundle:ExtraField') + ->findOneBy( + [ + 'variable' => 'moodle_password', + 'extraFieldType' => ExtraField::USER_FIELD_TYPE, + ] + ); + } + + /** + * @return ExtraFieldValues|null + */ + private function getExtraFieldValue(ExtraField $extraField, array $userData) + { + return Database::getManager() + ->getRepository('ChamiloCoreBundle:ExtraFieldValues') + ->findOneBy(['field' => $extraField, 'itemId' => $userData['user_id']]); + } +} diff --git a/plugin/migrationmoodle/src/MigrationMoodlePlugin.php b/plugin/migrationmoodle/src/MigrationMoodlePlugin.php new file mode 100644 index 000000000..4ddfcf26a --- /dev/null +++ b/plugin/migrationmoodle/src/MigrationMoodlePlugin.php @@ -0,0 +1,169 @@ + 'boolean', + 'db_host' => 'text', + 'db_user' => 'text', + 'db_password' => 'text', + 'db_name' => 'text', + self::SETTING_USER_FILTER => 'text', + self::SETTING_URL_ID => 'text', + self::SETTING_MOODLE_PATH => 'text', + ]; + + parent::__construct($version, $author, $settings); + } + + /** + * @return MigrationMoodlePlugin|null + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @throws \Doctrine\DBAL\DBALException + * + * @return Connection + */ + public function getConnection() + { + $params = [ + 'host' => $this->get('db_host'), + 'user' => $this->get('db_user'), + 'password' => $this->get('db_password'), + 'dbname' => $this->get('db_name'), + 'driver' => 'pdo_mysql', + ]; + + $connection = DriverManager::getConnection($params, new Configuration()); + + return $connection; + } + + /** + * Perform actions after configure the plugin. + * + * Add user extra field. + * + * @throws Exception + * + * @return MigrationMoodlePlugin + */ + public function performActionsAfterConfigure() + { + if ('true' === $this->get('active')) { + $this->installHook(); + } else { + $this->uninstallHook(); + } + + return $this; + } + + /** + * This method will call the Hook management insertHook to add Hook observer from this plugin. + * + * @throws Exception + * + * @return void + */ + public function installHook() + { + $hookObserver = MigrationMoodleCheckLoginCredentialsHook::create(); + + CheckLoginCredentialsHook::create()->attach($hookObserver); + } + + /** + * This method will call the Hook management deleteHook to disable Hook observer from this plugin. + * + * @throws Exception + * + * @return void + */ + public function uninstallHook() + { + $hookObserver = MigrationMoodleCheckLoginCredentialsHook::create(); + + CheckLoginCredentialsHook::create()->detach($hookObserver); + } + + /** + * @return string + */ + public function getUserFilterSetting() + { + $userFilter = $this->get(self::SETTING_USER_FILTER); + + return trim($userFilter); + } + + /** + * @return int + */ + public function getAccessUrlId() + { + $urlId = (int) $this->get(self::SETTING_URL_ID); + + return $urlId ?: 1; + } + + /** + * @return string + */ + public function getMoodledataPath() + { + $path = $this->get(self::SETTING_MOODLE_PATH); + + return rtrim($path, ' /'); + } + + /** + * @param string $name + * + * @return bool + */ + public function isTaskDone($name) + { + $result = Database::select( + 'COUNT(1) c', + 'plugin_migrationmoodle_task', + [ + 'where' => [ + 'name = ?' => Database::escape_string($name.'_task'), + 'or name = ?' => Database::escape_string($name.'_script'), + ], + ], + 'first' + ); + + return $result['c'] > 0; + } +} diff --git a/plugin/migrationmoodle/src/Script/BaseScript.php b/plugin/migrationmoodle/src/Script/BaseScript.php new file mode 100644 index 000000000..a49aff0e7 --- /dev/null +++ b/plugin/migrationmoodle/src/Script/BaseScript.php @@ -0,0 +1,94 @@ +calledClass = get_called_class(); + } + + public function run() + { + \Database::insert( + 'plugin_migrationmoodle_task', + ['name' => $this->getTaskName()] + ); + + $this->process(); + } + + abstract public function process(); + + /** + * @param int $userId + * + * @return bool + */ + protected function isLoadedUser($userId) + { + return $this->isLoadedId($userId, 'users_task'); + } + + /** + * @param int $lpId + * + * @return bool + */ + protected function isMigratedLearningPath($lpId) + { + return $this->isLoadedId($lpId, 'course_sections_task'); + } + + /** + * @param int $scormId + * + * @return bool + */ + protected function isMigratedScorm($scormId) + { + return $this->isLoadedId($scormId, 'course_modules_scorm_task'); + } + + /** + * @param string $message + */ + protected function showMessage($message) + { + echo '['.date(\DateTime::ATOM)."]\t$message".PHP_EOL; + } + + /** + * @param int $id + * @param string $taskName + * + * @return bool + */ + private function isLoadedId($id, $taskName) + { + $row = \Database::fetch_assoc( + \Database::query( + "SELECT COUNT(pmi.id) AS nbr + FROM plugin_migrationmoodle_item pmi + INNER JOIN plugin_migrationmoodle_task pmt ON pmi.task_id = pmt.id + WHERE pmt.name = '$taskName' AND pmi.loaded_id = $id" + ) + ); + + return $row['nbr'] > 0; + } +} diff --git a/plugin/migrationmoodle/src/Script/UserLearnPathsProgressScript.php b/plugin/migrationmoodle/src/Script/UserLearnPathsProgressScript.php new file mode 100644 index 000000000..b945f55f4 --- /dev/null +++ b/plugin/migrationmoodle/src/Script/UserLearnPathsProgressScript.php @@ -0,0 +1,132 @@ +countItemsInLp(); + + foreach ($this->getUsersAndLps() as $lpView) { + $userId = $lpView['user_id']; + $lpId = $lpView['lp_id']; + $cId = $lpView['c_id']; + $lpViewId = $lpView['iid']; + + $completedItems = $this->countCompletedItems($userId, $lpId, $cId); + + if (empty($completedItems) || empty($itemsInLp[$lpId])) { + continue; + } + + $progress = (int) ($completedItems / $itemsInLp[$lpId] * 100); + + \Database::query( + "UPDATE c_lp_view + SET progress = $progress + WHERE user_id = $userId + AND lp_id = $lpId + AND c_id = $cId + AND iid = $lpViewId" + ); + + $this->showMessage("Updated: c_lp_view $lpViewId with progress $progress."); + } + } + + /** + * @return array + */ + private function countItemsInLp() + { + $tblItem = \Database::get_course_table(TABLE_LP_ITEM); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query( + "SELECT lpi.lp_id, COUNT(lpi.iid) AS c_lpi + FROM $tblItem lpi + INNER JOIN $tblLp lp ON lpi.lp_id = lp.iid + WHERE lpi.item_type != 'dir' AND lp.lp_type = 1 + GROUP BY lpi.lp_id" + ); + + $data = []; + + while ($row = \Database::fetch_assoc($result)) { + $data[$row['lp_id']] = $row['c_lpi']; + } + + return $data; + } + + /** + * @return \Generator + */ + private function getUsersAndLps() + { + $tblLpView = \Database::get_course_table(TABLE_LP_VIEW); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query( + "SELECT lpv.iid, lpv.lp_id, lpv.user_id, lpv.c_id + FROM $tblLpView lpv + INNER JOIN $tblLp lp ON lpv.lp_id = lp.iid + INNER JOIN plugin_migrationmoodle_item pmi ON pmi.loaded_id = lpv.user_id + INNER JOIN plugin_migrationmoodle_task pmt ON pmi.task_id = pmt.id + WHERE lp.lp_type = 1 AND pmt.name = 'users_task'" + ); + + while ($row = \Database::fetch_assoc($result)) { + if (!$this->isLoadedUser($row['user_id']) || + !$this->isMigratedLearningPath($row['lp_id']) + ) { + continue; + } + + yield $row; + } + } + + /** + * @param int $userId + * @param int $lpId + * @param int $cId + * + * @return int + */ + private function countCompletedItems($userId, $lpId, $cId) + { + $tblItemView = \Database::get_course_table(TABLE_LP_ITEM_VIEW); + $tblLpView = \Database::get_course_table(TABLE_LP_VIEW); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query("SELECT + lpiv.lp_view_id, + lpiv.c_id, + COUNT(lpiv.lp_item_id) c_lpiv + FROM $tblItemView lpiv + INNER JOIN $tblLpView lpv ON (lpiv.lp_view_id = lpv.iid AND lpiv.c_id = lpv.c_id) + INNER JOIN $tblLp lp ON (lpv.lp_id = lp.iid AND lpv.c_id = lp.c_id) + WHERE lpiv.status = 'completed' + AND lpv.user_id = $userId + AND lp.iid = $lpId + AND lp.c_id = $cId + AND lp.lp_type = 1 + GROUP BY lpiv.lp_view_id"); + + $row = \Database::fetch_assoc($result); + + if (empty($row) || empty($row['c_lpiv'])) { + return 0; + } + + return $row['c_lpiv']; + } +} diff --git a/plugin/migrationmoodle/src/Script/UserScormsProgressScript.php b/plugin/migrationmoodle/src/Script/UserScormsProgressScript.php new file mode 100644 index 000000000..d878ef224 --- /dev/null +++ b/plugin/migrationmoodle/src/Script/UserScormsProgressScript.php @@ -0,0 +1,138 @@ +countItemsInLp(); + + foreach ($this->getUsersAndLps() as $lpView) { + $userId = $lpView['user_id']; + $lpId = $lpView['lp_id']; + $cId = $lpView['c_id']; + $lpViewId = $lpView['iid']; + + $completedItems = $this->countCompletedItems($userId, $lpId, $cId); + + if (empty($completedItems) || empty($itemsInLp[$lpId])) { + continue; + } + + if ($completedItems > $itemsInLp[$lpId]) { + $progress = 100; + } else { + $progress = (int) ($completedItems / $itemsInLp[$lpId] * 100); + } + + \Database::query( + "UPDATE c_lp_view + SET progress = $progress + WHERE user_id = $userId + AND lp_id = $lpId + AND c_id = $cId + AND iid = $lpViewId" + ); + + $this->showMessage("Updated: c_lp_view $lpViewId with progress $progress."); + } + } + + /** + * @return array + */ + private function countItemsInLp() + { + $tblItem = \Database::get_course_table(TABLE_LP_ITEM); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query( + "SELECT lpi.lp_id, COUNT(lpi.iid) AS c_lpi + FROM $tblItem lpi + INNER JOIN $tblLp lp ON lpi.lp_id = lp.iid + WHERE lpi.item_type = 'sco' AND lp.lp_type = 2 + GROUP BY lpi.lp_id" + ); + + $data = []; + + while ($row = \Database::fetch_assoc($result)) { + $data[$row['lp_id']] = $row['c_lpi']; + } + + return $data; + } + + /** + * @return \Generator + */ + private function getUsersAndLps() + { + $tblLpView = \Database::get_course_table(TABLE_LP_VIEW); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query( + "SELECT lpv.iid, lpv.lp_id, lpv.user_id, lpv.c_id + FROM $tblLpView lpv + INNER JOIN $tblLp lp ON lpv.lp_id = lp.iid + WHERE lp.lp_type = 2" + ); + + while ($row = \Database::fetch_assoc($result)) { + if (!$this->isLoadedUser($row['user_id']) || + !$this->isMigratedScorm($row['lp_id']) + ) { + continue; + } + + yield $row; + } + } + + /** + * @param int $userId + * @param int $lpId + * @param int $cId + * + * @return int + */ + private function countCompletedItems($userId, $lpId, $cId) + { + $tblItemView = \Database::get_course_table(TABLE_LP_ITEM_VIEW); + $tblLpView = \Database::get_course_table(TABLE_LP_VIEW); + $tblLp = \Database::get_course_table(TABLE_LP_MAIN); + + $result = \Database::query("SELECT + lpiv.lp_view_id, + lpiv.c_id, + COUNT(lpiv.lp_item_id) c_lpiv + FROM $tblItemView lpiv + INNER JOIN $tblLpView lpv ON (lpiv.lp_view_id = lpv.iid AND lpiv.c_id = lpv.c_id) + INNER JOIN $tblLp lp ON (lpv.lp_id = lp.iid AND lpv.c_id = lp.c_id) + WHERE lpiv.status = 'completed' + AND lpv.user_id = $userId + AND lp.iid = $lpId + AND lp.c_id = $cId + AND lp.lp_type = 2 + GROUP BY lpiv.lp_view_id, lpiv.lp_item_id"); + + if (\Database::num_rows($result) == 0) { + return 0; + } + + $count = 0; + + while ($row = \Database::fetch_assoc($result)) { + $count += (int) $row['c_lpiv']; + } + + return $count; + } +} diff --git a/plugin/migrationmoodle/src/Task/BaseTask.php b/plugin/migrationmoodle/src/Task/BaseTask.php new file mode 100644 index 000000000..962079c46 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/BaseTask.php @@ -0,0 +1,199 @@ +plugin = \MigrationMoodlePlugin::create(); + + $this->extractor = $this->getExtractor(); + + $this->transformer = $this->getTransformer(); + + $this->loader = $this->getLoader(); + + $this->calledClass = get_called_class(); + } + + /** + * @return array + */ + abstract public function getExtractConfiguration(); + + /** + * @return array + */ + abstract public function getTransformConfiguration(); + + /** + * @return array + */ + abstract public function getLoadConfiguration(); + + public function execute() + { + $outputBuffering = isset($GLOBALS['outputBuffering']) ? $GLOBALS['outputBuffering'] : true; + + $taskId = \Database::insert( + 'plugin_migrationmoodle_task', + ['name' => $this->getTaskName()] + ); + + $i = 0; + + foreach ($this->executeETL() as $hash => $ids) { + \Database::insert( + 'plugin_migrationmoodle_item', + [ + 'hash' => $hash, + 'extracted_id' => (int) $ids['extracted'], + 'loaded_id' => (int) $ids['loaded'], + 'task_id' => $taskId, + ] + ); + + echo "[".date(\DateTime::ATOM)."]\tData migrated: $hash".PHP_EOL; + + $i++; + + if ($i % 10 === 0 && $outputBuffering) { + flush(); + ob_flush(); + } + } + + if ($outputBuffering) { + ob_end_flush(); + } + } + + /** + * @return \Generator + */ + private function executeETL() + { + foreach ($this->extractFiltered() as $extractedData) { + try { + $incomingData = $this->transformer->transform($extractedData); + } catch (\Exception $exception) { + new TransformMessage($extractedData, $exception); + + continue; + } + + try { + $loadedId = $this->loader->load($incomingData); + } catch (\Exception $exception) { + new LoadMessage($incomingData, $exception); + + continue; + } + + yield md5("{$extractedData['id']}@@$loadedId") => [ + 'extracted' => $extractedData['id'], + 'loaded' => $loadedId, + ]; + } + } + + /** + * @return \Generator + */ + private function extractFiltered() + { + try { + foreach ($this->extractor->extract() as $extractedData) { + if ($this->extractor->filter($extractedData)) { + continue; + } + + yield $extractedData; + } + } catch (\Exception $exception) { + new ExtractMessage($exception); + } + } + + /** + * @return ExtractorInterface + */ + private function getExtractor() + { + $configuration = $this->getExtractConfiguration(); + + $extractorClass = $configuration['class']; + /** @var ExtractorInterface $extractor */ + $extractor = new $extractorClass($configuration); + + return $extractor; + } + + /** + * @return TransformerInterface + */ + private function getTransformer() + { + $configuration = $this->getTransformConfiguration(); + + $transformerClass = $configuration['class']; + /** @var TransformerInterface $extractor */ + $extractor = new $transformerClass($configuration); + + return $extractor; + } + + /** + * @return LoaderInterface + */ + private function getLoader() + { + $configuration = $this->getLoadConfiguration(); + + $loaderClass = $configuration['class']; + /** @var LoaderInterface $extractor */ + $extractor = new $loaderClass($configuration); + + return $extractor; + } +} diff --git a/plugin/migrationmoodle/src/Task/CQuizTask.php b/plugin/migrationmoodle/src/Task/CQuizTask.php new file mode 100644 index 000000000..e8b7365b9 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CQuizTask.php @@ -0,0 +1,77 @@ + BaseExtractor::class, + 'query' => 'SELECT * FROM mdl_quiz', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'exerciseTitle' => 'name', + 'exerciseDescription' => 'intro', + //'exerciseFeedbackType', + //'results_disabled', + //'exerciseType', + //'question_selection_type', + //'randomQuestions' => 'shufflequestions', + 'randomAnswers' => 'shuffleanswers', + //'display_category_name', + //'hide_question_title', + 'exerciseAttempts' => 'attempts', + //'activate_start_date_check', + 'start_time' => 'timeopen', + //'activate_end_date_check', + 'end_time' => 'timeclose', + //'enabletimercontrol', + 'enabletimercontroltotalminutes' => 'timelimit', + 'pass_percentage' => [ + 'class' => Percentage::class, + 'properties' => ['sumgrades', 'grade'], + ], + //'text_when_finished', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CQuizLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseCategoriesTask.php b/plugin/migrationmoodle/src/Task/CourseCategoriesTask.php new file mode 100644 index 000000000..63dcc3936 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseCategoriesTask.php @@ -0,0 +1,61 @@ + BaseExtractor::class, + 'query' => 'SELECT * FROM mdl_course_categories ORDER BY parent ASC, id ASC', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'name' => 'name', + 'code' => [ + 'class' => CourseCode::class, + 'properties' => ['name'], + ], + 'description' => 'description', + 'parent_id' => [ + 'class' => CourseCategoryLookup::class, + 'properties' => ['parent'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseCategoriesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseFilesTask.php b/plugin/migrationmoodle/src/Task/CourseFilesTask.php new file mode 100644 index 000000000..4fab46226 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseFilesTask.php @@ -0,0 +1,47 @@ + BaseTransformer::class, + 'map' => [ + 'contenthash' => 'contenthash', + 'filepath' => 'filepath', + 'filename' => 'filename', + 'filesize' => 'filesize', + 'mimetype' => 'mimetype', + 'course' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseFilesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseIntroductionsTask.php b/plugin/migrationmoodle/src/Task/CourseIntroductionsTask.php new file mode 100644 index 000000000..690071315 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseIntroductionsTask.php @@ -0,0 +1,64 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT id, course, name, summary + FROM mdl_course_sections + WHERE section = 0 AND (summary != '' AND summary IS NOT NULL)", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'name' => 'name', + 'description' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['summary', 'course'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseIntroductionLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseModulesLessonTask.php b/plugin/migrationmoodle/src/Task/CourseModulesLessonTask.php new file mode 100644 index 000000000..a0eebd9ef --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseModulesLessonTask.php @@ -0,0 +1,68 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT cm.id, l.course, l.name, cm.section + FROM mdl_lesson l + INNER JOIN mdl_course_modules cm ON (l.course = cm.course AND cm.instance = l.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'lesson' + ORDER BY cs.id, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionLookup::class, + 'properties' => ['section'], + ], + 'title' => 'name', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseModulesLessonLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseModulesQuizTask.php b/plugin/migrationmoodle/src/Task/CourseModulesQuizTask.php new file mode 100644 index 000000000..ff814d6c4 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseModulesQuizTask.php @@ -0,0 +1,68 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT cm.id, q.course, q.name, cm.section + FROM mdl_quiz q + INNER JOIN mdl_course_modules cm ON (q.course = cm.course AND cm.instance = q.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'quiz' + ORDER BY cs.id, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionLookup::class, + 'properties' => ['section'], + ], + 'title' => 'name', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseModulesQuizLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseModulesScormTask.php b/plugin/migrationmoodle/src/Task/CourseModulesScormTask.php new file mode 100644 index 000000000..715d21eb3 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseModulesScormTask.php @@ -0,0 +1,92 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + sco.id, + sco.course, + sco.name, + sco.reference, + sco.version, + sco.maxgrade, + sco.hidetoc, + i.identifier, + cm.added, + sco.timemodified + FROM mdl_scorm sco + INNER JOIN mdl_scorm_scoes i on sco.id = i.scorm + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + AND i.parent = '/' + ORDER BY cs.id, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'name' => 'name', + 'ref' => 'identifier', + 'path' => 'reference', + 'use_max_score' => 'maxgrade', + 'hide_toc_frame' => 'hidetoc', + 'created_on' => [ + 'class' => DateTimeObject::class, + 'properties' => ['added'], + ], + 'modified_on' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timemodified'], + ], + 'publicated_on' => [ + 'class' => DateTimeObject::class, + 'properties' => ['added'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseModulesScormLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseModulesUrlTask.php b/plugin/migrationmoodle/src/Task/CourseModulesUrlTask.php new file mode 100644 index 000000000..7adf7fa54 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseModulesUrlTask.php @@ -0,0 +1,75 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT cm.id, u.course, u.name, cm.section + FROM mdl_url u + INNER JOIN mdl_course_modules cm ON (u.course = cm.course AND cm.instance = u.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'url' + AND u.course NOT IN ( + SELECT sco.course + FROM mdl_scorm sco + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + )", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionLookup::class, + 'properties' => ['section'], + ], + 'title' => 'name', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseModulesUrlLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CourseSectionsTask.php b/plugin/migrationmoodle/src/Task/CourseSectionsTask.php new file mode 100644 index 000000000..19f350173 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CourseSectionsTask.php @@ -0,0 +1,74 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT id, course, name, summary + FROM mdl_course_sections + WHERE section > 0 AND (name != '' OR name IS NOT NULL) + AND course NOT IN ( + SELECT sco.course + FROM mdl_scorm sco + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + ) + ORDER BY course, section", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'course_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + 'name' => 'name', + 'description' => [ + 'class' => WrapHtmlReplacingFilePaths::class, + 'properties' => ['summary', 'course'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CourseSectionsLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/CoursesTask.php b/plugin/migrationmoodle/src/Task/CoursesTask.php new file mode 100644 index 000000000..8c8ca63fb --- /dev/null +++ b/plugin/migrationmoodle/src/Task/CoursesTask.php @@ -0,0 +1,85 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT DISTINCT c.* + FROM mdl_course c + INNER JOIN mdl_context ctx ON c.id = ctx.instanceid + INNER JOIN mdl_role_assignments ra ON ctx.id = ra.contextid + INNER JOIN mdl_user u ON ra.userid = u.id + WHERE u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => BaseExtractor::class, + 'query' => $query, + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'title' => 'fullname', + 'wanted_code' => 'shortname', + 'course_category' => [ + 'class' => CourseCategoryLookup::class, + 'properties' => ['category'], + ], + 'course_language' => [ + 'class' => Language::class, + 'properties' => ['lang'], + ], + 'visibility' => [ + 'class' => CourseVisibility::class, + 'properties' => ['visible'], + ], + 'description' => 'summary', + 'creation_date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timecreated'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => CoursesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForCourseIntroductionsTask.php b/plugin/migrationmoodle/src/Task/FilesForCourseIntroductionsTask.php new file mode 100644 index 000000000..0c6e2923a --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForCourseIntroductionsTask.php @@ -0,0 +1,41 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.filesize, + f.mimetype, + c.id course + FROM mdl_files f + INNER JOIN mdl_context ctx ON f.contextid = ctx.id + INNER JOIN mdl_course c ON ctx.instanceid = c.id + INNER JOIN mdl_course_sections cs ON (cs.course = c.id AND cs.id = f.itemid) + WHERE f.component = 'course' + AND f.filearea = 'section' + AND ctx.contextlevel = 50 + AND f.filename NOT IN ('.', '..') + AND cs.section = 0 AND (cs.summary != '' AND cs.summary IS NOT NULL)", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForCourseSectionsTask.php b/plugin/migrationmoodle/src/Task/FilesForCourseSectionsTask.php new file mode 100644 index 000000000..70a7928e0 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForCourseSectionsTask.php @@ -0,0 +1,49 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.filesize, + f.mimetype, + c.id course + FROM mdl_files f + INNER JOIN mdl_context ctx ON f.contextid = ctx.id + INNER JOIN mdl_course c ON ctx.instanceid = c.id + INNER JOIN mdl_course_sections cs ON (cs.course = c.id AND cs.id = f.itemid) + WHERE f.component = 'course' + AND f.filearea = 'section' + AND ctx.contextlevel = 50 + AND f.filename NOT IN ('.', '..') + AND cs.section > 0 + AND c.id NOT IN ( + SELECT sco.course + FROM mdl_scorm sco + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + )", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForLessonAnswersTask.php b/plugin/migrationmoodle/src/Task/FilesForLessonAnswersTask.php new file mode 100644 index 000000000..06ec494f5 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForLessonAnswersTask.php @@ -0,0 +1,41 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.filesize, + f.mimetype, + cm.course + FROM mdl_files f + INNER JOIN mdl_context c ON f.contextid = c.id + INNER JOIN mdl_course_modules cm ON c.instanceid = cm.id + WHERE f.component = 'mod_lesson' + AND f.filearea = 'page_answers' + AND c.contextlevel = 70 + AND f.filename NOT IN ('.', '..')", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForLessonPagesTask.php b/plugin/migrationmoodle/src/Task/FilesForLessonPagesTask.php new file mode 100644 index 000000000..1cd52a633 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForLessonPagesTask.php @@ -0,0 +1,41 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.filesize, + f.mimetype, + cm.course + FROM mdl_files f + INNER JOIN mdl_context c ON f.contextid = c.id + INNER JOIN mdl_course_modules cm ON c.instanceid = cm.id + WHERE f.component = 'mod_lesson' + AND f.filearea = 'page_contents' + AND c.contextlevel = 70 + AND f.filename NOT IN ('.', '..')", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForQuizzesTask.php b/plugin/migrationmoodle/src/Task/FilesForQuizzesTask.php new file mode 100644 index 000000000..c7e675875 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForQuizzesTask.php @@ -0,0 +1,39 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.filesize, + f.mimetype, + cm.course + FROM mdl_files f + INNER JOIN mdl_context c ON f.contextid = c.id + INNER JOIN mdl_course_modules cm ON c.instanceid = cm.id + WHERE f.component = 'mod_quiz' + AND f.filearea = 'intro' + AND c.contextlevel = 70 + AND f.filename NOT IN ('.', '..')", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/FilesForScormScoesTask.php b/plugin/migrationmoodle/src/Task/FilesForScormScoesTask.php new file mode 100644 index 000000000..93609b9e2 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/FilesForScormScoesTask.php @@ -0,0 +1,83 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + f.id, + f.contenthash, + f.filepath, + f.filename, + f.mimetype, + s.name scorm_name, + cm.course + FROM mdl_files f + INNER JOIN mdl_context ctx ON f.contextid = ctx.id + INNER JOIN mdl_course_modules cm ON ctx.instanceid = cm.id + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_scorm s ON (cm.course = s.course AND cm.instance = s.id) + WHERE + m.name = 'scorm' + AND ctx.contextlevel = 70 + AND f.filename NOT IN ('.', '..') + AND s.reference != f.filename + AND f.filearea = 'content' + AND f.component = 'mod_scorm' + ORDER BY s.course, s.id", + ]; + } + + /** + * {@inheritdoc} + */ + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'contenthash' => 'contenthash', + 'filepath' => 'filepath', + 'filename' => 'filename', + 'mimetype' => 'mimetype', + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'lp_name' => 'scorm_name', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => FilesForScormScoLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersEssayTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersEssayTask.php new file mode 100644 index 000000000..ddd19ca76 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersEssayTask.php @@ -0,0 +1,70 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT la.id, la.pageid, la.score, l.course + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype = 10 + ORDER BY lp.id', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['pageid'], + ], + 'question_id' => [ + 'class' => LoadedLessonPageQuizQuestionLookup::class, + 'properties' => ['pageid'], + ], + 'score' => 'score', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersEssayLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersMatchingTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersMatchingTask.php new file mode 100644 index 000000000..982c546f9 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersMatchingTask.php @@ -0,0 +1,83 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT la.id, la.pageid, la.answer, la.response, la.lessonid, l.course + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype = 5 + AND (la.response IS NOT NULL OR la.response != '') + ORDER BY lp.id", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['pageid'], + ], + 'question_id' => [ + 'class' => LoadedLessonPageQuizQuestionLookup::class, + 'properties' => ['pageid'], + ], + 'score' => [ + 'class' => LessonAnswersMatchingScore::class, + 'properties' => ['pageid', 'lessonid', 'course'], + ], + 'answer' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['answer', 'course'], + ], + 'feedback' => 'response', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersMatchingLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersMultipleAnswerTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersMultipleAnswerTask.php new file mode 100644 index 000000000..4d75e97ea --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersMultipleAnswerTask.php @@ -0,0 +1,43 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT la.id, la.pageid, la.score, la.answer, la.response, l.course + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype = 3 AND lp.qoption = 1 + ORDER BY lp.id', + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersMultipleAnswerLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersMultipleChoiceTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersMultipleChoiceTask.php new file mode 100644 index 000000000..2d689928c --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersMultipleChoiceTask.php @@ -0,0 +1,43 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT la.id, la.pageid, la.score, la.answer, la.response, l.course + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype = 3 AND lp.qoption = 0 + ORDER BY lp.id', + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersMultipleChoiceLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersShortAnswerTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersShortAnswerTask.php new file mode 100644 index 000000000..9e995e511 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersShortAnswerTask.php @@ -0,0 +1,83 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + la.id, + la.pageid, + GROUP_CONCAT(la.answer SEPARATOR '||') answers, + GROUP_CONCAT(la.response SEPARATOR '') comment, + MAX(la.score) scores, + l.course, + COUNT(la.pageid) nb + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype IN (1, 8) + GROUP BY lp.id + ORDER BY lp.id", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['pageid'], + ], + 'question_id' => [ + 'class' => LoadedLessonPageQuizQuestionLookup::class, + 'properties' => ['pageid'], + ], + 'scores' => 'scores', + 'answers' => 'answers', + 'comment' => 'comment', + 'nb' => 'nb', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersShortAnswerLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonAnswersTrueFalseTask.php b/plugin/migrationmoodle/src/Task/LessonAnswersTrueFalseTask.php new file mode 100644 index 000000000..c8a8cd1a7 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonAnswersTrueFalseTask.php @@ -0,0 +1,85 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT la.id, la.pageid, la.score, la.answer, la.response, l.course, + ( + SELECT MIN(id) = la.id + FROM mdl_lesson_answers + WHERE pageid = la.pageid + GROUP BY lessonid, pageid + ) is_correct + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson_pages lp ON (la.pageid = lp.id AND la.lessonid = lp.lessonid) + INNER JOIN mdl_lesson l ON (lp.lessonid = l.id AND la.lessonid = l.id) + WHERE lp.qtype = 2 + ORDER BY lp.id', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['pageid'], + ], + 'question_id' => [ + 'class' => LoadedLessonPageQuizQuestionLookup::class, + 'properties' => ['pageid'], + ], + 'score' => 'score', + 'answer' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['answer', 'course'], + ], + 'feedback' => 'response', + 'is_correct' => 'is_correct', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersTrueFalseLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonPagesDocumentTask.php b/plugin/migrationmoodle/src/Task/LessonPagesDocumentTask.php new file mode 100644 index 000000000..4cf51b2d5 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonPagesDocumentTask.php @@ -0,0 +1,75 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT lp.id, l.id lessonid, l.course, lp.title, lp.contents + FROM mdl_lesson_pages lp + INNER JOIN mdl_lesson l ON lp.lessonid = l.id + WHERE lp.qtype = 20', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionFromLessonLookup::class, + 'properties' => ['lessonid'], + ], + 'item_id' => [ + 'class' => LoadedLessonPageLookup::class, + 'properties' => ['id'], + ], + 'item_title' => 'title', + 'item_content' => [ + 'class' => WrapHtmlReplacingFilePaths::class, + 'properties' => ['contents', 'course'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonPagesDocumentLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonPagesQuizQuestionTask.php b/plugin/migrationmoodle/src/Task/LessonPagesQuizQuestionTask.php new file mode 100644 index 000000000..1c89aa39f --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonPagesQuizQuestionTask.php @@ -0,0 +1,77 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT lp.id, l.course, lp.contents, lp.qoption, lp.qtype + FROM mdl_lesson_pages lp + INNER JOIN mdl_lesson l ON lp.lessonid = l.id + WHERE lp.qtype IN (1, 2, 3, 5, 8, 10)', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['id'], + ], + 'question_title' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['contents', 'course'], + ], + 'question_type' => [ + 'class' => QuizQuestionTypeFromLessonPage::class, + 'properties' => ['qtype', 'qoption'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonQuestionPagesQuestionLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonPagesQuizTask.php b/plugin/migrationmoodle/src/Task/LessonPagesQuizTask.php new file mode 100644 index 000000000..760438dad --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonPagesQuizTask.php @@ -0,0 +1,65 @@ + LoadedCoursesFilterExtractor::class, + 'query' => 'SELECT lp.id, l.course, lp.title + FROM mdl_lesson_pages lp + INNER JOIN mdl_lesson l ON lp.lessonid = l.id + WHERE lp.qtype IN (1, 2, 3, 5, 8, 10)', + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'item_id' => [ + 'class' => LoadedLessonPageLookup::class, + 'properties' => ['id'], + ], + 'item_title' => 'title', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonQuestionPagesQuizLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/LessonPagesTask.php b/plugin/migrationmoodle/src/Task/LessonPagesTask.php new file mode 100644 index 000000000..66a3da4db --- /dev/null +++ b/plugin/migrationmoodle/src/Task/LessonPagesTask.php @@ -0,0 +1,104 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "WITH RECURSIVE lesson_pages_ordered (id, title, qtype, prev, next, lesson, display_order) AS + ( + SELECT id, title, qtype, prevpageid, nextpageid, lessonid, '01' + FROM mdl_lesson_pages + WHERE prevpageid = 0 + UNION + SELECT lp.id, lp.title, lp.qtype, lp.prevpageid, lp.nextpageid, lp.lessonid, lpo.display_order + 1 + FROM lesson_pages_ordered lpo + LEFT JOIN mdl_lesson_pages lp + ON (lpo.next = lp.id AND lpo.lesson = lp.lessonid) + ) + SELECT + lpo.id, + lpo.title, + lpo.qtype, + lpo.prev, + lpo.lesson, + CAST(lpo.display_order AS SIGNED) lpo_display_order, + l.course + FROM lesson_pages_ordered lpo + INNER JOIN mdl_lesson l ON lpo.lesson = l.id + WHERE lpo.qtype NOT IN (".self::TYPE_END_BRANCH.", ".self::TYPE_CLUSTER.", ".self::TYPE_END_CLUSTER.") + ORDER BY l.course, lpo.lesson, lpo_display_order", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionFromLessonLookup::class, + 'properties' => ['lesson'], + ], + 'parent' => [ + 'class' => LoadedCourseModuleLessonLookup::class, + 'properties' => ['lesson'], + ], + 'previous' => [ + 'class' => LoadedLessonPageLookup::class, + 'properties' => ['prev'], + ], + 'item_type' => [ + 'class' => LessonPageType::class, + 'properties' => ['qtype'], + ], + 'title' => 'title', + 'display_order' => 'lpo_display_order', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonPagesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionCategoriesTask.php b/plugin/migrationmoodle/src/Task/QuestionCategoriesTask.php new file mode 100644 index 000000000..d94f4ba4c --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionCategoriesTask.php @@ -0,0 +1,62 @@ + BaseExtractor::class, + 'query' => "SELECT qc.id, qc.name, qc.info, c.contextlevel, c.instanceid + FROM mdl_question_categories qc + INNER JOIN mdl_context c ON qc.contextid = c.id + WHERE c.contextlevel IN (50, 70) + AND qc.parent != 0", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseFromQuestionCategoryLookup::class, + 'properties' => ['contextlevel', 'instanceid'], + ], + 'name' => 'name', + 'description' => 'info', + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => QuestionCategoriesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionGapselectTask.php b/plugin/migrationmoodle/src/Task/QuestionGapselectTask.php new file mode 100644 index 000000000..3cb0aeeb8 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionGapselectTask.php @@ -0,0 +1,90 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + qa.id, + qq.id, + qa.question, + GROUP_CONCAT( + CONCAT(qa.feedback, '==>>', qa.answer) ORDER BY qa.id ASC SEPARATOR '@||@' + ) answers, + qq.questiontext, + qs.maxmark, + qg.correctfeedback, + q.id quiz_id, + q.course + FROM mdl_question_answers qa + INNER JOIN mdl_question qq ON qa.question = qq.id + INNER JOIN mdl_question_gapselect qg ON qq.id = qg.questionid + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + WHERE qq.qtype = 'gapselect' + GROUP BY q.id, qq.id + ORDER BY q.id, qq.id", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedQuizLookup::class, + 'properties' => ['quiz_id'], + ], + 'question_id' => [ + 'class' => LoadedQuestionLookup::class, + 'properties' => ['question'], + ], + 'answer' => [ + 'class' => QuestionGapselectAnswer::class, + 'properties' => ['answers', 'questiontext', 'maxmark'], + ], + 'score' => 'maxmark', + 'comment' => 'correctfeedback', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => QuestionGapselectLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionMultiChoiceMultipleTask.php b/plugin/migrationmoodle/src/Task/QuestionMultiChoiceMultipleTask.php new file mode 100644 index 000000000..7e41f0e3a --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionMultiChoiceMultipleTask.php @@ -0,0 +1,51 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + qa.id, + qa.question, + qa.answer, + qa.feedback, + (qa.fraction * qq.defaultmark) score, + IF (qa.fraction > 0, TRUE, FALSE) is_correct, + q.id quizid, + q.course + FROM mdl_question_answers qa + INNER JOIN mdl_question qq ON qa.question = qq.id + INNER JOIN mdl_qtype_multichoice_options qo ON qq.id = qo.questionid + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + WHERE qq.qtype = 'multichoice' + AND qo.single = 0", + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersMultipleAnswerLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionMultiChoiceSingleTask.php b/plugin/migrationmoodle/src/Task/QuestionMultiChoiceSingleTask.php new file mode 100644 index 000000000..eb4ac7afb --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionMultiChoiceSingleTask.php @@ -0,0 +1,92 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + qa.id, + qa.question, + qa.answer, + qa.feedback, + (qa.fraction * qq.defaultmark) score, + IF (qa.fraction = 1, TRUE, FALSE) is_correct, + q.id quizid, + q.course + FROM mdl_question_answers qa + INNER JOIN mdl_question qq ON qa.question = qq.id + INNER JOIN mdl_qtype_multichoice_options qo ON qq.id = qo.questionid + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + WHERE qq.qtype = 'multichoice' + AND qo.single = 1", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['quizid'], + ], + 'question_id' => [ + 'class' => LoadedQuestionLookup::class, + 'properties' => ['question'], + ], + 'score' => 'score', + 'answer' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['answer', 'course'], + ], + 'feedback' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['feedback', 'course'], + ], + 'is_correct' => 'is_correct', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonAnswersMultipleChoiceLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionShortAnswerTask.php b/plugin/migrationmoodle/src/Task/QuestionShortAnswerTask.php new file mode 100644 index 000000000..0b788d1bd --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionShortAnswerTask.php @@ -0,0 +1,73 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + qa.id, + qa.question, + GROUP_CONCAT(qa.answer SEPARATOR '||') answers, + qq.defaultmark, + GROUP_CONCAT(qa.feedback SEPARATOR '\n') feedback, + COUNT(qa.id) nb, + q.id quizid, + q.course + FROM mdl_question_answers qa + INNER JOIN mdl_question qq ON qa.question = qq.id + INNER JOIN mdl_qtype_shortanswer_options qo ON qq.id = qo.questionid + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + WHERE qq.qtype = 'shortanswer' + GROUP BY qq.id + ORDER BY q.course, qq.id", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedLessonPageQuizLookup::class, + 'properties' => ['quizid'], + ], + 'question_id' => [ + 'class' => LoadedQuestionLookup::class, + 'properties' => ['question'], + ], + 'scores' => 'defaultmark', + 'answers' => 'answers', + 'comment' => 'feedback', + 'nb' => 'nb', + ], + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionsTask.php b/plugin/migrationmoodle/src/Task/QuestionsTask.php new file mode 100644 index 000000000..f2c7754df --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionsTask.php @@ -0,0 +1,76 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT qq.id, qq.category, qq.questiontext, qq.qtype, q.course, q.id quiz_id + FROM mdl_question qq + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + INNER JOIN mdl_course_modules cm ON (q.course = cm.course AND cm.instance = q.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'quiz'", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'quiz_id' => [ + 'class' => LoadedQuizLookup::class, + 'properties' => ['quiz_id'], + ], + 'question_title' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['questiontext', 'course'], + ], + 'question_type' => [ + 'class' => QuestionType::class, + 'properties' => ['qtype', 'id'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => LessonQuestionPagesQuestionLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuestionsTrueFalseTask.php b/plugin/migrationmoodle/src/Task/QuestionsTrueFalseTask.php new file mode 100644 index 000000000..52aecf84f --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuestionsTrueFalseTask.php @@ -0,0 +1,40 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + qa.id, + qa.question, + qa.answer, + qa.feedback, + (qa.fraction * qq.defaultmark) score, + IF (qa.fraction = 1, TRUE, FALSE) is_correct, + q.id quizid, + q.course + FROM mdl_question_answers qa + INNER JOIN mdl_question qq ON qa.question = qq.id + INNER JOIN mdl_quiz_slots qs ON qq.id = qs.questionid + INNER JOIN mdl_quiz q ON qs.quizid = q.id + WHERE qq.qtype = 'truefalse'", + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuizzesScoresTask.php b/plugin/migrationmoodle/src/Task/QuizzesScoresTask.php new file mode 100644 index 000000000..c2ecd1af0 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuizzesScoresTask.php @@ -0,0 +1,74 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + q.id, + q.course, + cm.section, + cm.id cm_id + FROM mdl_quiz q + INNER JOIN mdl_course_modules cm ON (q.course = cm.course AND cm.instance = q.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'quiz' + ORDER BY cs.id, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'quiz_id' => [ + 'class' => LoadedQuizLookup::class, + 'properties' => ['id'], + ], + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'item_id' => [ + 'class' => LoadedCourseModuleQuizLookup::class, + 'properties' => ['cm_id'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => QuizzesScoresLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/QuizzesTask.php b/plugin/migrationmoodle/src/Task/QuizzesTask.php new file mode 100644 index 000000000..03a0269be --- /dev/null +++ b/plugin/migrationmoodle/src/Task/QuizzesTask.php @@ -0,0 +1,97 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT + q.id, + q.course, + q.name, + q.intro, + q.shuffleanswers, + q.attempts, + q.timeopen, + q.timeclose, + q.timelimit, + gi.gradepass, + q.grade, + cm.id cm_id + FROM mdl_quiz q + INNER JOIN mdl_course_modules cm ON (q.course = cm.course AND cm.instance = q.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + INNER JOIN mdl_grade_items gi ON (q.id = gi.iteminstance AND m.name = gi.itemmodule) + WHERE m.name = 'quiz' + AND gi.itemtype = 'mod' + ORDER BY cs.id, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'item_id' => [ + 'class' => LoadedCourseModuleQuizLookup::class, + 'properties' => ['cm_id'], + ], + 'exerciseTitle' => 'name', + 'exerciseDescription' => [ + 'class' => ReplaceFilePaths::class, + 'properties' => ['intro', 'course'], + ], + 'randomAnswers' => 'shuffleanswers', + 'exerciseAttempts' => 'attempts', + 'start_time' => 'timeopen', + 'end_time' => 'timeclose', + 'enabletimercontroltotalminutes' => 'timelimit', + 'pass_percentage' => [ + 'class' => Percentage::class, + 'properties' => ['gradepass', 'grade'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => QuizzesLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/RoleAssignmentsTask.php b/plugin/migrationmoodle/src/Task/RoleAssignmentsTask.php new file mode 100644 index 000000000..985c9138c --- /dev/null +++ b/plugin/migrationmoodle/src/Task/RoleAssignmentsTask.php @@ -0,0 +1,72 @@ + BaseExtractor::class, + 'query' => 'SELECT ra.id, r.archetype, ra.userid, c.id cid + FROM mdl_role_assignments ra + INNER JOIN mdl_role r ON ra.roleid = r.id + INNER JOIN mdl_context ctx ON ra.contextid = ctx.id + INNER JOIN mdl_course c ON ctx.instanceid = c.id + WHERE ctx.contextlevel = '.self::CONTEXT_LEVEL_COURSE, + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'status' => [ + 'class' => CourseUserStatus::class, + 'properties' => ['archetype'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'course_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['cid'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => RoleAssignmentsLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/ScormScoesTask.php b/plugin/migrationmoodle/src/Task/ScormScoesTask.php new file mode 100644 index 000000000..8e3a6ccb8 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/ScormScoesTask.php @@ -0,0 +1,71 @@ + LoadedScormsFilterExtractor::class, + 'query' => "SELECT i.id, i.title, i.scormtype, i.launch, i.identifier, i.scorm, i.parent, s.course + FROM mdl_scorm_scoes i + INNER JOIN mdl_scorm s ON i.scorm = s.id + WHERE i.parent != '/' ORDER BY s.id, i.sortorder", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'title' => 'title', + 'item_type' => 'scormtype', + 'path' => 'launch', + 'ref' => 'identifier', + 'lp_id' => [ + 'class' => LoadedScormLookup::class, + 'properties' => ['scorm'], + ], + 'parent_item_id' => [ + 'class' => ScormScoParentLookup::class, + 'properties' => ['parent', 'scorm'], + ], + 'c_code' => [ + 'class' => LoadedCourseCodeLookup::class, + 'properties' => ['course'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => ScormScoLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/SortSectionModulesTask.php b/plugin/migrationmoodle/src/Task/SortSectionModulesTask.php new file mode 100644 index 000000000..2fb72649e --- /dev/null +++ b/plugin/migrationmoodle/src/Task/SortSectionModulesTask.php @@ -0,0 +1,78 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT cm.id, cs.course, cm.section, cs.sequence + FROM mdl_course_modules cm + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE cs.course NOT IN ( + SELECT sco.course + FROM mdl_scorm sco + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + ) + ORDER BY cs.course, cs.section, FIND_IN_SET(cm.id, cs.sequence)", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionLookup::class, + 'properties' => ['section'], + ], + 'order_list' => [ + 'class' => SectionSequenceLookup::class, + 'properties' => ['sequence'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => SortSectionModuleLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/TrackCourseAccessTask.php b/plugin/migrationmoodle/src/Task/TrackCourseAccessTask.php new file mode 100644 index 000000000..66fb69699 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/TrackCourseAccessTask.php @@ -0,0 +1,86 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT lsl.id, lsl.userid, lsl.courseid, lsl.timecreated, lsl.ip + FROM mdl_logstore_standard_log lsl + INNER JOIN mdl_user u ON lsl.userid = u.id + WHERE (lsl.courseid IS NOT NULL AND lsl.courseid != 0) + AND u.username LIKE '$userFilter%' + ORDER BY lsl.timecreated"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['courseid'], + ], + 'login_course_date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timecreated'], + ], + 'ip' => 'ip', + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => TrackCourseAccessLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/TrackLoginTask.php b/plugin/migrationmoodle/src/Task/TrackLoginTask.php new file mode 100644 index 000000000..5904313ab --- /dev/null +++ b/plugin/migrationmoodle/src/Task/TrackLoginTask.php @@ -0,0 +1,71 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query .= " AND username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'login_user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['id'], + ], + 'login_date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['firstaccess'], + ], + 'logout_date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['lastaccess'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => TrackLoginLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UrlsTask.php b/plugin/migrationmoodle/src/Task/UrlsTask.php new file mode 100644 index 000000000..064eb1a02 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UrlsTask.php @@ -0,0 +1,79 @@ + LoadedCoursesFilterExtractor::class, + 'query' => "SELECT u.id, u.course, u.name, u.externalurl, cm.section, cm.id cm_id + FROM mdl_url u + INNER JOIN mdl_course_modules cm ON (u.course = cm.course AND cm.instance = u.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'url' + AND u.course NOT IN ( + SELECT sco.course + FROM mdl_scorm sco + INNER JOIN mdl_course_modules cm ON (sco.course = cm.course AND cm.instance = sco.id) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_course_sections cs ON (cm.course = cs.course AND cm.section = cs.id ) + WHERE m.name = 'scorm' + )", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'lp_id' => [ + 'class' => LoadedCourseSectionLookup::class, + 'properties' => ['section'], + ], + 'item_id' => [ + 'class' => LoadedCourseModuleUrlLookup::class, + 'properties' => ['cm_id'], + ], + 'title' => 'name', + 'url' => 'externalurl', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UrlLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UserQuestionAttemptsGapselectTask.php b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsGapselectTask.php new file mode 100644 index 000000000..a85cf3be0 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsGapselectTask.php @@ -0,0 +1,22 @@ +questionType = 'gapselect'; + + return parent::getExtractConfiguration(); + } +} diff --git a/plugin/migrationmoodle/src/Task/UserQuestionAttemptsShortanswerTask.php b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsShortanswerTask.php new file mode 100644 index 000000000..07cdbd36b --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsShortanswerTask.php @@ -0,0 +1,22 @@ +questionType = 'shortanswer'; + + return parent::getExtractConfiguration(); + } +} diff --git a/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTask.php b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTask.php new file mode 100644 index 000000000..86afdee76 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTask.php @@ -0,0 +1,124 @@ +plugin->getUserFilterSetting(); + + $userFilterCondition = !empty($userFilter) ? "AND u.username LIKE '$userFilter%'" : ''; + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => "SELECT + qqa.id, + qa.userid, + qa.id quiz_attempt, + qqa.questionid, + qqa.questionsummary, + qqa.rightanswer, + qqa.responsesummary, + qqa.timemodified, + qqas.fraction, + q.course, + qq.defaultmark, + qq.qtype + FROM mdl_question_attempts qqa + INNER JOIN mdl_quiz_attempts qa ON qqa.questionusageid = qa.uniqueid + INNER JOIN mdl_question_attempt_steps qqas + ON (qqa.id = qqas.questionattemptid AND qa.userid = qqas.userid) + INNER JOIN mdl_question_attempt_step_data qqasd ON qqas.id = qqasd.attemptstepid + INNER JOIN mdl_question qq ON (qqa.questionid = qq.id) + INNER JOIN mdl_quiz q ON (qa.quiz = q.id) + INNER JOIN mdl_user u ON (qqas.userid = u.id) + WHERE qqas.state NOT IN ('todo', 'complete') + AND (qqasd.name = '-finish' AND qqasd.value = 1) + AND qq.qtype = '{$this->questionType}' + $userFilterCondition + ORDER BY qa.userid, qa.quiz, qqa.slot", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'exe_id' => [ + 'class' => LoadedUserQuizLookup::class, + 'properties' => ['quiz_attempt'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'question_id' => [ + 'class' => LoadedQuestionLookup::class, + 'properties' => ['questionid'], + ], + 'answer' => [ + 'class' => UserQuestionAnswer::class, + 'properties' => [ + 'qtype', + 'rightanswer', + 'responsesummary', + 'fraction', + 'defaultmark', + 'questionsummary', + 'questionid', + ], + ], + 'marks' => 'fraction', + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'tms' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timemodified'], + ], + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserQuestionAttemptLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTruefalseTask.php b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTruefalseTask.php new file mode 100644 index 000000000..f781f57a3 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UserQuestionAttemptsTruefalseTask.php @@ -0,0 +1,22 @@ +questionType = 'truefalse'; + + return parent::getExtractConfiguration(); + } +} diff --git a/plugin/migrationmoodle/src/Task/UserSessionsTask.php b/plugin/migrationmoodle/src/Task/UserSessionsTask.php new file mode 100644 index 000000000..1651bdf92 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UserSessionsTask.php @@ -0,0 +1,87 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $userFilterCondition = "AND u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => "SELECT + u.id, + u.username, + GROUP_CONCAT(c.id) course_ids, + GROUP_CONCAT(c.shortname SEPARATOR '".self::SEPARATOR_NAME."') session_name + FROM mdl_role_assignments ra + INNER JOIN mdl_role r ON ra.roleid = r.id + INNER JOIN mdl_context ctx ON ra.contextid = ctx.id + INNER JOIN mdl_course c ON ctx.instanceid = c.id + INNER JOIN mdl_user u ON ra.userid = u.id + WHERE ctx.contextlevel = ".RoleAssignmentsTask::CONTEXT_LEVEL_COURSE." + $userFilterCondition + GROUP BY ra.userid", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'name' => [ + 'class' => SessionName::class, + 'properties' => ['username', 'session_name'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['id'], + ], + 'courses_list' => 'session_name', + 'course_ids' => [ + 'class' => CoursesArrayLookup::class, + 'properties' => ['course_ids'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserSessionLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLastLoginTask.php b/plugin/migrationmoodle/src/Task/UsersLastLoginTask.php new file mode 100644 index 000000000..1e933a275 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLastLoginTask.php @@ -0,0 +1,68 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT id, lastlogin FROM mdl_user + WHERE lastlogin != 0 AND username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['id'], + ], + 'last_login' => [ + 'class' => DateTimeObject::class, + 'properties' => ['lastlogin'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLastLoginLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonAttemptsTask.php b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonAttemptsTask.php new file mode 100644 index 000000000..fbd37f35d --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonAttemptsTask.php @@ -0,0 +1,58 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT la.* FROM mdl_lesson_attempts la + INNER JOIN mdl_user u ON la.userid = u.id + WHERE u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + $config = parent::getTransformConfiguration(); + + $config['map']['is_correct'] = 'correct'; + + return $config; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLearnPathLessonAttemptLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonBranchTask.php b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonBranchTask.php new file mode 100644 index 000000000..1c7463e02 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonBranchTask.php @@ -0,0 +1,101 @@ += lesson_timer.starttime AND lb.timeseen <= lesson_timer.lessontime + ORDER BY lb.userid, lb.lessonid, lb.timeseen'; + + $userFilter = $this->plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT lb.* FROM mdl_lesson_branch lb + INNER JOIN ( + SELECT lt.lessonid, lt.userid, lt.starttime, lt.lessontime + FROM mdl_lesson_timer lt + INNER JOIN mdl_user u ON (lt.userid = u.id) + WHERE lt.completed = 1 + AND u.username LIKE '$userFilter%' + GROUP BY lt.userid, lt.lessonid + ORDER BY lt.userid, lt.lessonid, lt.starttime + ) AS lesson_timer ON (lb.lessonid = lesson_timer.lessonid AND lb.userid = lesson_timer.userid) + INNER JOIN mdl_user u ON (lb.userid = u.id) + WHERE lb.timeseen >= lesson_timer.starttime AND lb.timeseen <= lesson_timer.lessontime + AND u.username LIKE '$userFilter%' + ORDER BY lb.userid, lb.lessonid, lb.timeseen"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'parent_item_id' => [ + 'class' => LoadedCourseModuleLessonLookup::class, + 'properties' => ['lessonid'], + ], + 'item_id' => [ + 'class' => LoadedLessonPageLookup::class, + 'properties' => ['pageid'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'end_time' => 'timeseen', + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + 'view_count' => 'retry', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLearnPathLessonBranchLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonTimerTask.php b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonTimerTask.php new file mode 100644 index 000000000..a07f21410 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLearnPathsLessonTimerTask.php @@ -0,0 +1,83 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT lt.* FROM mdl_lesson_timer lt + INNER JOIN mdl_user u ON lt.userid = u.id + WHERE u.username LIKE '$userFilter%' + AND lt.completed = 1 + GROUP BY lt.userid, lt.lessonid + ORDER BY lt.userid, lt.lessonid, lt.starttime"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'parent_item_id' => [ + 'class' => LoadedCourseModuleLessonLookup::class, + 'properties' => ['lessonid'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'start_time' => 'starttime', + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLearnPathLessonTimerLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLearnPathsQuizzesTask.php b/plugin/migrationmoodle/src/Task/UsersLearnPathsQuizzesTask.php new file mode 100644 index 000000000..7f1590aba --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLearnPathsQuizzesTask.php @@ -0,0 +1,90 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT qa.id, qa.quiz, qa.userid, qa.timestart, qa.timefinish, qa.state, qa.sumgrades + FROM mdl_quiz_attempts qa + INNER JOIN mdl_user u ON qa.userid = u.id + WHERE qa.preview = 0 + AND u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => $query, + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'item_id' => [ + 'class' => LoadedCourseModuleQuizByQuizLookup::class, + 'properties' => ['quiz'], + ], + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + 'start_time' => 'timestart', + 'total_time' => [ + 'class' => Subtract::class, + 'properties' => ['timefinish', 'timestart'], + ], + 'status' => [ + 'class' => LearnPathItemViewQuizStatus::class, + 'properties' => ['quiz', 'state', 'sumgrades'], + ], + 'score' => 'sumgrades', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLearnPathQuizLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersLearnPathsTask.php b/plugin/migrationmoodle/src/Task/UsersLearnPathsTask.php new file mode 100644 index 000000000..5b4a1bfc3 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersLearnPathsTask.php @@ -0,0 +1,64 @@ +plugin->getUserFilterSetting(); + + $userFilterCondition = ''; + + if (!empty($userFilter)) { + $userFilterCondition = "WHERE username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => "SELECT id FROM mdl_user $userFilterCondition", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['id'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserLearnPathsLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersQuizzesAttemptsTask.php b/plugin/migrationmoodle/src/Task/UsersQuizzesAttemptsTask.php new file mode 100644 index 000000000..3e30afb9f --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersQuizzesAttemptsTask.php @@ -0,0 +1,102 @@ +plugin->getUserFilterSetting(); + $userCondition = ''; + + if (!empty($userFilter)) { + $userCondition = "INNER JOIN mdl_user u ON qa.userid = u.id WHERE u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => "SELECT + qa.id, + qa.quiz, + qa.userid, + qa.layout, + qa.state, + qa.timestart, + qa.timefinish, + qa.sumgrades real_result, + q.sumgrades weighting + FROM mdl_quiz_attempts qa + INNER JOIN mdl_quiz q ON qa.quiz = q.id + $userCondition + ORDER BY qa.userid, q.id, qa.attempt", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timestart'], + ], + 'exo_id' => [ + 'class' => LoadedQuizLookup::class, + 'properties' => ['quiz'], + ], + 'result' => 'real_result', + 'weighting' => 'weighting', + 'data_tracking' => [ + 'class' => QuizDataTracking::class, + 'properties' => ['quiz', 'layout'], + ], + 'session_id' => [ + 'class' => LoadedUserSessionLookup::class, + 'properties' => ['userid'], + ], + 'duration' => [ + 'class' => Subtract::class, + 'properties' => ['timefinish', 'timestart'], + ], + 'status' => 'state', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UserQuizAttemptLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersScormsViewTask.php b/plugin/migrationmoodle/src/Task/UsersScormsViewTask.php new file mode 100644 index 000000000..e64c54cc2 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersScormsViewTask.php @@ -0,0 +1,98 @@ +plugin->getUserFilterSetting(); + + $userCondition = ''; + + if (!empty($userFilter)) { + $userCondition = "INNER JOIN mdl_user u ON sst.userid = u.id WHERE u.username LIKE '$userFilter%'"; + } + + return [ + 'class' => LoadedUsersFilterExtractor::class, + 'query' => "SELECT + sst.id, + sst.userid, + s.id scormid, + ss.id scoid, + sst.attempt, + s.course, + GROUP_CONCAT(sst.element, '==>>', sst.value ORDER BY sst.id SEPARATOR '|@|') track_data + FROM mdl_scorm_scoes_track sst + INNER JOIN mdl_scorm_scoes ss ON (sst.scoid = ss.id AND sst.scormid = ss.scorm) + INNER JOIN mdl_scorm s ON (ss.scorm = s.id) + $userCondition + GROUP BY sst.userid, s.id, ss.id, sst.attempt + ORDER BY sst.userid, s.course, s.id", + ]; + } + + /** + * {@inheritdoc} + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'user_id' => [ + 'class' => LoadedUserLookup::class, + 'properties' => ['userid'], + ], + 'lp_id' => [ + 'class' => LoadedScormLookup::class, + 'properties' => ['scormid'], + ], + 'lp_item_id' => [ + 'class' => LoadedScormScoLookup::class, + 'properties' => ['scoid'], + ], + 'lp_item_view_count' => 'attempt', + 'c_id' => [ + 'class' => LoadedCourseLookup::class, + 'properties' => ['course'], + ], + 'item_data' => [ + 'class' => ScormScoTrackData::class, + 'properties' => ['track_data'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function getLoadConfiguration() + { + return [ + 'class' => UsersScormsViewLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Task/UsersTask.php b/plugin/migrationmoodle/src/Task/UsersTask.php new file mode 100644 index 000000000..d819c8c90 --- /dev/null +++ b/plugin/migrationmoodle/src/Task/UsersTask.php @@ -0,0 +1,91 @@ +plugin->getUserFilterSetting(); + + if (!empty($userFilter)) { + $query = "SELECT * FROM mdl_user WHERE username LIKE '$userFilter%'"; + } + + return [ + 'class' => BaseExtractor::class, + 'query' => $query, + ]; + } + + /** + * @return array + */ + public function getTransformConfiguration() + { + return [ + 'class' => BaseTransformer::class, + 'map' => [ + 'lastname' => 'lastname', + 'firstname' => 'firstname', + 'email' => 'email', + 'username' => 'username', + 'plain_password' => 'password', + 'language' => 'lang', + 'phone' => 'phone1', + 'address' => 'address', + 'auth_source' => [ + 'class' => AuthLookup::class, + 'properties' => ['auth'], + ], + 'registration_date' => [ + 'class' => DateTimeObject::class, + 'properties' => ['timecreated'], + ], + 'status' => [ + 'class' => UserStatus::class, + 'properties' => ['id'], + ], + 'active' => [ + 'class' => UserActive::class, + 'properties' => ['deleted', 'suspended'], + ], + 'enabled' => [ + 'class' => UserActive::class, + 'properties' => ['deleted', 'suspended'], + ], + ], + ]; + } + + /** + * @return array + */ + public function getLoadConfiguration() + { + return [ + 'class' => UsersLoader::class, + ]; + } +} diff --git a/plugin/migrationmoodle/src/Traits/FileFinderTrait.php b/plugin/migrationmoodle/src/Traits/FileFinderTrait.php new file mode 100644 index 000000000..d4f8608d1 --- /dev/null +++ b/plugin/migrationmoodle/src/Traits/FileFinderTrait.php @@ -0,0 +1,35 @@ +getMoodledataPath(); + + $filePath = "$moodleDataPath/filedir/$d1/$d2/$contentHash"; + + if (!file_exists($filePath)) { + throw new \Exception("File $contentHash not found in $moodleDataPath/filedir"); + } + + return $filePath; + } +} diff --git a/plugin/migrationmoodle/src/Traits/MapTrait.php b/plugin/migrationmoodle/src/Traits/MapTrait.php new file mode 100644 index 000000000..71924fefd --- /dev/null +++ b/plugin/migrationmoodle/src/Traits/MapTrait.php @@ -0,0 +1,22 @@ +calledClass, '\\'), 1); + + return api_camel_case_to_underscore($name); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/BaseTransformer.php b/plugin/migrationmoodle/src/Transformer/BaseTransformer.php new file mode 100644 index 000000000..d9b3f0836 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/BaseTransformer.php @@ -0,0 +1,55 @@ +map = $configuration['map']; + } + + /** + * @throws \Exception + * + * @return array + */ + public function transform(array $sourceData) + { + $incomingResult = []; + + foreach ($this->map as $incomingProperty => $sourceProperty) { + if (is_array($sourceProperty)) { + $transformerClass = $sourceProperty['class']; + /** @var TransformerInterface $transformer */ + $transformer = new $transformerClass(); + $sourceProperties = $sourceProperty['properties']; + } else { + $transformer = new Copy(); + $sourceProperties = [$sourceProperty]; + } + + $data = []; + + foreach ($sourceProperties as $sourcePropertyName) { + $data[$sourcePropertyName] = $sourceData[$sourcePropertyName]; + } + + $incomingResult[$incomingProperty] = $transformer->transform($data); + } + + return $incomingResult; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/AuthLookup.php b/plugin/migrationmoodle/src/Transformer/Property/AuthLookup.php new file mode 100644 index 000000000..6fa81f411 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/AuthLookup.php @@ -0,0 +1,22 @@ +calledClass = CourseCategoriesTask::class; + } + + /** + * @throws \Exception + * + * @return string|null + */ + public function transform(array $data) + { + $categoryId = parent::transform($data); + + $category = \Database::getManager()->find('ChamiloCoreBundle:CourseCategory', $categoryId); + + if (empty($category)) { + return null; + } + + return $category->getCode(); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/CourseCode.php b/plugin/migrationmoodle/src/Transformer/Property/CourseCode.php new file mode 100644 index 000000000..ed06e4580 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/CourseCode.php @@ -0,0 +1,30 @@ +setTimezone(new \DateTimeZone('UTC')); + $date->setTimestamp($timeCreated); + + return $date; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/Explode.php b/plugin/migrationmoodle/src/Transformer/Property/Explode.php new file mode 100644 index 000000000..323178a06 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/Explode.php @@ -0,0 +1,26 @@ + 'english', + 'fr' => 'french', + 'es' => 'spanish', + ]; + + /** + * @return string + */ + public function transform(array $data) + { + $language = current($data); + + if (array_key_exists($language, $this->languages)) { + return $this->languages[$language]; + } + + return $this->languages['en']; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LearnPathItemViewQuizStatus.php b/plugin/migrationmoodle/src/Transformer/Property/LearnPathItemViewQuizStatus.php new file mode 100644 index 000000000..a55636950 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LearnPathItemViewQuizStatus.php @@ -0,0 +1,58 @@ +findQuiz($quizId); + + if ('finished' === $mQuizAttemptState) { + if ($quiz->getPassPercentage() > 0 && $mQuizSumGrades > $quiz->getPassPercentage()) { + return 'passed'; + } + + return 'completed'; + } + + return 'not attempted'; + } + + /** + * @param int $quizId + * + * @throws \Exception + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + * + * @return \Chamilo\CourseBundle\Entity\CQuiz + */ + private function findQuiz($quizId = 0) + { + $quiz = \Database::getManager()->find('ChamiloCourseBundle:CQuiz', $quizId); + + if (!$quiz) { + throw new \Exception("Quiz ($quizId) not found."); + } + + return $quiz; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LessonAnswersMatchingScore.php b/plugin/migrationmoodle/src/Transformer/Property/LessonAnswersMatchingScore.php new file mode 100644 index 000000000..7e15ef542 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LessonAnswersMatchingScore.php @@ -0,0 +1,59 @@ +getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + try { + $query = "SELECT + la.score, + COUNT(IF(score = 0 AND response IS NOT NULL, 1, NULL)) 'count' + FROM mdl_lesson_answers la + INNER JOIN mdl_lesson l ON (la.lessonid = l.id) + WHERE la.pageid = ? + AND la.lessonid = ? + AND l.course = ?"; + + $result = $connection->fetchAssoc($query, [$pageid, $lessonid, $course]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"{$this->query}\".", 0, $e); + } + + $connection->close(); + + $score = (float) $result['score']; + $count = (int) $result['count']; + + if (0 === $count) { + return 0; + } + + return $score / $count; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LessonPageType.php b/plugin/migrationmoodle/src/Transformer/Property/LessonPageType.php new file mode 100644 index 000000000..80660aacb --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LessonPageType.php @@ -0,0 +1,36 @@ +calledClass = CoursesTask::class; + } + + /** + * @throws \Exception + * + * @return string + */ + public function transform(array $data) + { + $cId = parent::transform($data); + + return api_get_course_entity($cId)->getCode(); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseFromQuestionCategoryLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseFromQuestionCategoryLookup.php new file mode 100644 index 000000000..83810f236 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseFromQuestionCategoryLookup.php @@ -0,0 +1,79 @@ +searchInModuleContext($instanceId); + break; + } + + if (empty($mCourseId)) { + throw new \Exception("Course not found for context $instanceId with level $contextLevel"); + } + + return parent::transform([$mCourseId]); + } + + /** + * @param int $instanceId + * + * @throws \Exception + * + * @return int + */ + private function searchInModuleContext($instanceId) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT course FROM mdl_course_modules WHERE id = ?"; + + try { + $statement = $connection->executeQuery($query, [$instanceId]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $result = $statement->fetch(FetchMode::ASSOCIATIVE); + + $connection->close(); + + if (false === $result) { + return 0; + } + + return (int) $result['course']; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseLookup.php new file mode 100644 index 000000000..aa9636fbe --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseLookup.php @@ -0,0 +1,22 @@ +calledClass = CoursesTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleLessonLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleLessonLookup.php new file mode 100644 index 000000000..995de8b34 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleLessonLookup.php @@ -0,0 +1,55 @@ +calledClass = CourseModulesLessonTask::class; + } + + public function transform(array $data) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT cm.id FROM mdl_course_modules cm + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_lesson l ON (cm.course = l.course AND cm.instance = l.id) + WHERE m.name = 'lesson' + AND l.id = ?"; + + $lessonId = current($data); + + try { + $statement = $connection->executeQuery($query, [$lessonId]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $result = $statement->fetch(FetchMode::ASSOCIATIVE); + + $connection->close(); + + $lessonId = $result['id']; + + return parent::transform([$lessonId]); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizByQuizLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizByQuizLookup.php new file mode 100644 index 000000000..ab2868870 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizByQuizLookup.php @@ -0,0 +1,60 @@ +calledClass = CourseModulesQuizTask::class; + } + + /** + * @throws \Exception + * + * @return mixed + */ + public function transform(array $data) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT cm.id FROM mdl_course_modules cm + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_quiz q ON (cm.course = q.course AND cm.instance = q.id) + WHERE m.name = 'quiz' + AND q.id = ?"; + + $lessonId = current($data); + + try { + $statement = $connection->executeQuery($query, [$lessonId]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $result = $statement->fetch(FetchMode::ASSOCIATIVE); + + $connection->close(); + + $lessonId = $result['id']; + + return parent::transform([$lessonId]); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizLookup.php new file mode 100644 index 000000000..1d84bbaa5 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleQuizLookup.php @@ -0,0 +1,22 @@ +calledClass = CourseModulesQuizTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleUrlLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleUrlLookup.php new file mode 100644 index 000000000..370b812a6 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseModuleUrlLookup.php @@ -0,0 +1,22 @@ +calledClass = CourseModulesUrlTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionFromLessonLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionFromLessonLookup.php new file mode 100644 index 000000000..9aa39d9d5 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionFromLessonLookup.php @@ -0,0 +1,52 @@ +getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT cs.id FROM mdl_course_sections cs + INNER JOIN mdl_course_modules cm ON (cs.id = cm.section AND cs.course = cm.course) + INNER JOIN mdl_modules m ON cm.module = m.id + INNER JOIN mdl_lesson l ON (cm.course = l.course AND cm.instance = l.id) + WHERE m.name = 'lesson' + AND l.id = ?"; + + $lessonId = current($data); + + try { + $statement = $connection->executeQuery($query, [$lessonId]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $result = $statement->fetch(FetchMode::ASSOCIATIVE); + + $connection->close(); + + $sectionId = $result['id']; + + return parent::transform([$sectionId]); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionLookup.php new file mode 100644 index 000000000..f01550ecd --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedCourseSectionLookup.php @@ -0,0 +1,22 @@ +calledClass = CourseSectionsTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedKeyLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedKeyLookup.php new file mode 100644 index 000000000..afe0e1d75 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedKeyLookup.php @@ -0,0 +1,60 @@ +search($id); + + return isset($migration['loaded_id']) ? $migration['loaded_id'] : 0; + } + + /** + * @param int $searchedId + * + * @throws \Exception + * + * @return array + */ + private function search($searchedId) + { + if (empty($searchedId)) { + return null; + } + + $taskName = $this->getTaskName(); + + $itemInfo = \Database::select( + 'i.*', + 'plugin_migrationmoodle_item i INNER JOIN plugin_migrationmoodle_task t ON i.task_id = t.id', + [ + 'where' => [ + 't.name = ? AND i.extracted_id = ?' => [$taskName, $searchedId], + ], + ], + 'first' + ); + + return $itemInfo ?: null; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageLookup.php new file mode 100644 index 000000000..4a046ab5a --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageLookup.php @@ -0,0 +1,22 @@ +calledClass = LessonPagesTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizLookup.php new file mode 100644 index 000000000..b1eff72de --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizLookup.php @@ -0,0 +1,24 @@ +calledClass = LessonPagesQuizTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizQuestionLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizQuestionLookup.php new file mode 100644 index 000000000..7bf157acd --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedLessonPageQuizQuestionLookup.php @@ -0,0 +1,24 @@ +calledClass = LessonPagesQuizQuestionTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedQuestionLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedQuestionLookup.php new file mode 100644 index 000000000..43aea916f --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedQuestionLookup.php @@ -0,0 +1,22 @@ +calledClass = QuestionsTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedQuizLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedQuizLookup.php new file mode 100644 index 000000000..5e388ac5f --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedQuizLookup.php @@ -0,0 +1,22 @@ +calledClass = QuizzesTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedScormLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedScormLookup.php new file mode 100644 index 000000000..a3816ff63 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedScormLookup.php @@ -0,0 +1,22 @@ +calledClass = CourseModulesScormTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedScormScoLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedScormScoLookup.php new file mode 100644 index 000000000..869d67e94 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedScormScoLookup.php @@ -0,0 +1,22 @@ +calledClass = ScormScoesTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedUserLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserLookup.php new file mode 100644 index 000000000..3123a2c62 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserLookup.php @@ -0,0 +1,21 @@ +calledClass = UsersTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedUserQuizLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserQuizLookup.php new file mode 100644 index 000000000..ad99a5dd4 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserQuizLookup.php @@ -0,0 +1,22 @@ +calledClass = UsersQuizzesAttemptsTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/LoadedUserSessionLookup.php b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserSessionLookup.php new file mode 100644 index 000000000..95e150949 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/LoadedUserSessionLookup.php @@ -0,0 +1,22 @@ +calledClass = UserSessionsTask::class; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/Percentage.php b/plugin/migrationmoodle/src/Transformer/Property/Percentage.php new file mode 100644 index 000000000..49c93614c --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/Percentage.php @@ -0,0 +1,28 @@ + $groupAndOption) { + $position = $zeroPosition + 1; + + list($group, $option) = explode('==>>', $groupAndOption); + + $positionsByGroup[$position] = $group; + $groups[$group][$position] = $option; + } + + $blanks = []; + + foreach ($positionsByGroup as $option => $group) { + if ($option === 1) { + $blanks[] = '['.implode($groups[$group], '|').']'; + + continue; + } + + $baz = $groups[$group]; + + unset($baz[$option]); + + $baz = [$groups[$group][$option]] + $baz; + + $blanks[] = '['.implode($baz, '|').']'; + } + + $countBlanks = 0; + + foreach ($blanks as $zeroPosition => $blank) { + $countBlank = 0; + $position = $zeroPosition + 1; + $questionText = str_replace("[[$position]]", $blank, $questionText, $countBlank); + + $countBlanks += $countBlank; + } + + $individualScore = $score / $countBlanks; + $scores = []; + + for ($i = 0; $i < $countBlanks - 1; $i++) { + $scores[] = $individualScore; + } + + $scores[] = $score - array_sum($scores); + + $inputs = str_repeat('300,', $countBlanks - 1).'300'; + + return utf8_encode("$questionText::".implode(',', $scores).":$inputs:0@"); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/QuestionType.php b/plugin/migrationmoodle/src/Transformer/Property/QuestionType.php new file mode 100644 index 000000000..c8e06f30a --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/QuestionType.php @@ -0,0 +1,86 @@ +isMultiChoiceSingle($id)) { + return MULTIPLE_ANSWER; + } + // no break + case 'truefalse': + return UNIQUE_ANSWER; + case 'match': + return MATCHING_DRAGGABLE; + case 'shortanswer': + case 'numerical': + case 'gapselect': + return FILL_IN_BLANKS; + case 'essay': + return FREE_ANSWER; + case 'calculated': + return CALCULATED_ANSWER; + case 'calculatedmulti': + case 'calculatedsimple': + case 'ddwtos': + case 'ddmarker': + case 'ddimageortext': + case 'multianswer': + case 'randomsamatch': + case 'description': + throw new \Exception("Question type \"$qtype\" not supported in question \"$id\"."); + } + } + + /** + * @param int $id + * + * @throws \Exception + * + * @return bool + */ + private function isMultiChoiceSingle($id) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT single FROM mdl_qtype_multichoice_options WHERE questionid = ?"; + + try { + $result = $connection->fetchAssoc($query, [$id]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $connection->close(); + + if (false === $result || !empty($result['single'])) { + return true; + } + + return false; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/QuizDataTracking.php b/plugin/migrationmoodle/src/Transformer/Property/QuizDataTracking.php new file mode 100644 index 000000000..6fc50442f --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/QuizDataTracking.php @@ -0,0 +1,68 @@ +findQuestionBySlotInQuiz($mQuizId, $mQuestionSlot); + + $tracking[] = parent::transform([$mQuestionId]); + } + + return implode(',', $tracking); + } + + /** + * @param int $quizId + * @param int $slot + * + * @throws \Exception + * + * @return mixed + */ + private function findQuestionBySlotInQuiz($quizId = 0, $slot = 0) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $exception) { + throw new \Exception('Unable to start connection.', 0, $exception); + } + + try { + $sql = "SELECT questionid FROM mdl_quiz_slots WHERE slot = ? AND quizid = ?"; + + $result = $connection->fetchAssoc($sql, [$slot, $quizId]); + } catch (DBALException $exception) { + throw new \Exception("Unable to execute query '{$this->query}'.", 0, $exception); + } + + $connection->close(); + + return $result['questionid']; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/QuizQuestionTypeFromLessonPage.php b/plugin/migrationmoodle/src/Transformer/Property/QuizQuestionTypeFromLessonPage.php new file mode 100644 index 000000000..9555372fd --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/QuizQuestionTypeFromLessonPage.php @@ -0,0 +1,52 @@ +loadHTML( + htmlentities($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') + ); + + foreach ($doc->getElementsByTagName('img') as $img) { + $this->getNewSource('src', $img, $courseInfo['path']); + } + + foreach ($doc->getElementsByTagName('a') as $a) { + $this->getNewSource('href', $a, $courseInfo['path']); + } + + $body = $doc->getElementsByTagName('body')->item(0); + $bodyHtml = $doc->saveHTML($body); + + return $this->removeBodyTags($bodyHtml); + } + + /** + * @param string $attribute + * @param string $coursePath + * + * @return string + */ + private function getNewSource($attribute, \DOMElement $domElement, $coursePath) + { + $source = $domElement->getAttribute($attribute); + + if (empty($source) || strpos($source, '@@PLUGINFILE@@') === false) { + return; + } + + $parsedUrl = parse_url($source); + $urlPath = $parsedUrl['path']; + $urlQuery = isset($parsedUrl['query']) ? $parsedUrl['query'] : ''; + + $fileName = basename($urlPath); + $fileName = urldecode($fileName); + $fileName = \URLify::filter($fileName, 250, '', true, true, false, false); + + $newSource = "/courses/$coursePath/document/$fileName" + .(!empty($urlQuery) ? "?$urlQuery" : ''); + + $domElement->setAttribute($attribute, $newSource); + } + + /** + * @param string $bodyHtml + * + * @return false|string + */ + private function removeBodyTags($bodyHtml) + { + $tagStart = ''; + $tagEnd = ''; + + return $content = substr( + $bodyHtml, + strlen($tagStart), + -1 * strlen($tagEnd) + ); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/ScormScoParentLookup.php b/plugin/migrationmoodle/src/Transformer/Property/ScormScoParentLookup.php new file mode 100644 index 000000000..39bb32c96 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/ScormScoParentLookup.php @@ -0,0 +1,41 @@ + [ + 'lp_id = ? AND ref = ?' => [$lpId, $data['parent']], + ], + ], + 'first' + ); + + if (!empty($lpItem)) { + return $lpItem['iid']; + } + + return 0; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/ScormScoTrackData.php b/plugin/migrationmoodle/src/Transformer/Property/ScormScoTrackData.php new file mode 100644 index 000000000..f4bbf6d34 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/ScormScoTrackData.php @@ -0,0 +1,130 @@ +>'; + + /** + * {@inheritdoc} + */ + public function transform(array $data) + { + $trackData = current($data); + $strComponents = explode(self::SEPARATOR_COMPONENTS, $trackData); + + $trackData = []; + + foreach ($strComponents as $strComponent) { + list($component, $value) = explode(self::SEPARATOR_VALUES, $strComponent); + + $trackData[$component] = $value; + } + + $elements = [ + 'x.start.time' => 'start_time', + 'cmi.core.lesson_status' => 'status', + 'cmi.core.total_time' => 'total_time', + 'cmi.core.exit' => 'core_exit', + 'cmi.suspend_data' => 'suspend_data', + 'cmi.core.score.raw' => 'score', + 'cmi.core.score.max' => 'max_score', + 'cmi.total_time' => 'total_time', + 'cmi.score.scaled' => 'score', + 'cmi.completion_status' => 'status', + ]; + + $itemData = []; + + foreach ($trackData as $component => $value) { + if ('cmi.core.total_time' === $component) { + $value = $this->hmsToSeconds($value); + } elseif ('cmi.core.exit' === $component) { + $value = $this->coreExit($value); + } elseif ('cmi.total_time' === $component) { + if (empty($trackData['x.start.time'])) { + $value = 0; + } else { + $value = $this->cmiTotalTime($trackData['x.start.time'], $value); + } + } elseif ('cmi.score.scaled' === $component) { + $value = $this->cmiScoreScaled($value); + } + + if (isset($elements[$component])) { + $variable = $elements[$component]; + + $itemData[$variable] = $value; + } + } + + return $itemData; + } + + /** + * @param string $hms + * + * @return int + */ + private function hmsToSeconds($hms) + { + if (empty($hms)) { + return 0; + } + + list($h, $m, $s) = explode(':', $hms); + + return ((int) $h * 3600) + ((int) $m * 60) + ceil($s); + } + + /** + * @param string $value + * + * @return string + */ + private function coreExit($value) + { + if (empty($value)) { + return 'none'; + } + } + + /** + * @param int $startTimeValue + * @param string $value + * + * @throws \Exception + * + * @return int + */ + private function cmiTotalTime($startTimeValue, $value) + { + $startTime = new \DateTime(); + $startTime->setTimestamp($startTimeValue); + $startTime->setTimezone(new \DateTimeZone('UTC')); + + $endTime = clone $startTime; + $endTime->add(new \DateInterval($value)); + + return $endTime->getTimestamp() - $startTime->getTimestamp(); + } + + /** + * @param string $value + * + * @return float + */ + private function cmiScoreScaled($value) + { + return (float) $value * 100; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/SectionSequenceLookup.php b/plugin/migrationmoodle/src/Transformer/Property/SectionSequenceLookup.php new file mode 100644 index 000000000..eedebdc67 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/SectionSequenceLookup.php @@ -0,0 +1,71 @@ +getConnection(); + } catch (DBALException $exception) { + throw new \Exception('Unable to start connection.', 0, $exception); + } + + $mSequence = explode(',', $data['sequence']); + $lpOrderList = []; + + foreach ($mSequence as $mModuleId) { + try { + $query = "SELECT m.name + FROM mdl_course_modules cm + INNER JOIN mdl_modules m ON cm.module = m.id + WHERE cm.id = ?"; + + $result = $connection->fetchAssoc($query, [$mModuleId]); + } catch (DBALException $exception) { + throw new \Exception("Unable to execute query \"{$this->query}\".", 0, $exception); + } + + if (empty($result)) { + continue; + } + + switch ($result['name']) { + case 'lesson': + $this->calledClass = CourseModulesLessonTask::class; + break; + case 'quiz': + $this->calledClass = CourseModulesQuizTask::class; + break; + default: + break; + } + + $lpItemId = parent::transform([$mModuleId]); + + $lpOrderList[$lpItemId] = 0; + } + + $connection->close(); + + return $lpOrderList; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/SessionName.php b/plugin/migrationmoodle/src/Transformer/Property/SessionName.php new file mode 100644 index 000000000..35c88947f --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/SessionName.php @@ -0,0 +1,30 @@ +transform($data); + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerGapselect.php b/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerGapselect.php new file mode 100644 index 000000000..2b40a5679 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerGapselect.php @@ -0,0 +1,139 @@ +gapselectGetRightAnswers($mRightAnswer); + + $mResponseSummary = $this->gapselectGetResponseSummary($mResponseSummary); + + $mQuestionSummary = explode(";", $mQuestionSummary); + $mQuestionSummary = array_map('trim', $mQuestionSummary); + + $questionText = array_shift($mQuestionSummary); + + $groupsAndOptions = $this->gapselectGetGroupsAndOptions($mQuestionSummary); + + $blanks = $this->gapselectGetBlanks($mRightAnswer, $groupsAndOptions); + + $count = 0; + + foreach ($blanks as $placeholder => $blank) { + $userAnswer = empty($mResponseSummary[$count]) ? '' : $mResponseSummary[$count]; + $replacement = $blank."[$userAnswer][0]"; + + $questionText = str_replace("[[$placeholder]]", $replacement, $questionText); + + $count++; + } + + $scorePerBlank = $mDefaultMark / count($mRightAnswer); + + $optionsScores = array_fill(0, count($mRightAnswer), $scorePerBlank); + $width = array_fill(0, count($mRightAnswer), 300); + + return utf8_encode("$questionText::".implode(',', $optionsScores).':'.implode(',', $width).':0@'); + } + + /** + * @param string $mRightAnswer + * + * @return array + */ + private function gapselectGetRightAnswers($mRightAnswer) + { + $rightAnswers = []; + + $mRightAnswer = explode('} {', $mRightAnswer); + + foreach ($mRightAnswer as $i0 => $item) { + $rightAnswers[$i0 + 1] = trim($item, "{} \t\n\r\x0B"); + } + + return $rightAnswers; + } + + /** + * @param string $mResponseSummary + * + * @return array + */ + private function gapselectGetResponseSummary($mResponseSummary) + { + $mResponseSummary = explode('} {', $mResponseSummary); + + return array_map( + function ($item) { + return trim($item, "{} \t\n\r\x0B"); + }, + $mResponseSummary + ); + } + + /** + * @return array + */ + private function gapselectGetGroupsAndOptions(array $mQuestionSummary) + { + $groupsAndOptions = []; + + foreach ($mQuestionSummary as $groupAndOptions) { + list($group, $options) = explode(' -> ', $groupAndOptions); + + $group = str_replace(['[', ']'], '', $group); + $options = explode(' / ', trim($options, "{} \t\n\r\x0B")); + + $groupsAndOptions[$group] = $options; + } + + return $groupsAndOptions; + } + + /** + * @return array + */ + private function gapselectGetBlanks(array $rightAnswers, array $groupsAndOptions) + { + $blanks = []; + + foreach ($rightAnswers as $i => $rightAnswer) { + foreach ($groupsAndOptions as $group => $options) { + if (in_array($rightAnswer, $options)) { + $optionIndex = array_search($rightAnswer, $options); + + unset($options[$optionIndex]); + + $options = array_merge([$rightAnswer], $options); + + $blanks[$i] = '['.implode('|', $options).']'; + } + } + } + + return $blanks; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerShortanswer.php b/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerShortanswer.php new file mode 100644 index 000000000..775ba3029 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/UserQuestionAnswerShortanswer.php @@ -0,0 +1,35 @@ +calledClass = QuestionsTask::class; + } + + /** + * {@inheritdoc} + */ + public function transform(array $data) + { + list( + $mQType, + $mRightAnswer, + $mResponseSummary, + $mFraction, + $mDefaultMark, + $mQuestionSummary, + $mQuestionId + ) = array_values($data); + + $questionId = parent::transform([$mQuestionId]); + + $answer = \Database::select( + 'iid', + \Database::get_course_table(TABLE_QUIZ_ANSWER), + [ + 'where' => [ + 'question_id = ? AND answer = ?' => [$questionId, utf8_encode("

    $mResponseSummary

    ")], + ], + ], + 'first' + ); + + if (empty($answer)) { + return 0; + } + + return $answer['iid']; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/UserStatus.php b/plugin/migrationmoodle/src/Transformer/Property/UserStatus.php new file mode 100644 index 000000000..b40469656 --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/UserStatus.php @@ -0,0 +1,62 @@ + ['manager', 'coursecreator', 'editingteacher', 'teacher'], + User::STUDENT => ['student', 'user'], + INVITEE => ['guest'], + ]; + + /** + * @throws \Exception + * + * @return int + */ + public function transform(array $sourceData) + { + try { + $connection = \MigrationMoodlePlugin::create()->getConnection(); + } catch (DBALException $e) { + throw new \Exception('Unable to start connection.', 0, $e); + } + + $query = "SELECT DISTINCT r.archetype FROM mdl_role r + INNER JOIN mdl_role_assignments ra ON r.id = ra.roleid WHERE ra.userid = ?"; + + $userId = (int) $sourceData['id']; + + try { + $statement = $connection->executeQuery($query, [$userId]); + } catch (DBALException $e) { + throw new \Exception("Unable to execute query \"$query\".", 0, $e); + } + + $userRoles = $statement->fetchAll(FetchMode::ASSOCIATIVE); + + $connection->close(); + + foreach (self::ROLES as $chamiloRole => $moodleRoles) { + foreach ($userRoles as $userRole) { + if (in_array($userRole['archetype'], $moodleRoles)) { + return $chamiloRole; + } + } + } + + return User::STUDENT; + } +} diff --git a/plugin/migrationmoodle/src/Transformer/Property/WrapHtmlReplacingFilePaths.php b/plugin/migrationmoodle/src/Transformer/Property/WrapHtmlReplacingFilePaths.php new file mode 100644 index 000000000..a234dde2d --- /dev/null +++ b/plugin/migrationmoodle/src/Transformer/Property/WrapHtmlReplacingFilePaths.php @@ -0,0 +1,36 @@ +$style$content"; + + return $content; + } +} diff --git a/plugin/migrationmoodle/uninstall.php b/plugin/migrationmoodle/uninstall.php new file mode 100644 index 000000000..a31446500 --- /dev/null +++ b/plugin/migrationmoodle/uninstall.php @@ -0,0 +1,57 @@ +uninstallHook(); +} catch (Exception $exception) { + $message = sprintf( + $plugin->get_lang('UninstallError'), + $exception->getMessage() + ); + + echo Display::return_message($message, 'error'); +} + +/** + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ +function removeExtraField() +{ + $em = Database::getManager(); + + /** @var ExtraField $extraField */ + $extraField = $em + ->getRepository('ChamiloCoreBundle:ExtraField') + ->findOneBy(['variable' => 'moodle_password', 'extraFieldType' => ExtraField::USER_FIELD_TYPE]); + + if ($extraField) { + $em + ->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') + ->execute(['field' => $extraField]); + + $em->remove($extraField); + $em->flush(); + } +} + +/** + * Drop database table created by this plugin. + */ +function removePluginTables() +{ + $queries = []; + $queries[] = "DROP TABLE IF EXISTS plugin_migrationmoodle_item"; + $queries[] = "DROP TABLE IF EXISTS plugin_migrationmoodle_task"; + + foreach ($queries as $query) { + Database::query($query); + } +} diff --git a/plugin/mindmap/ajax/mindmap.ajax.php b/plugin/mindmap/ajax/mindmap.ajax.php new file mode 100644 index 000000000..3ca192ea0 --- /dev/null +++ b/plugin/mindmap/ajax/mindmap.ajax.php @@ -0,0 +1,59 @@ + $dataMap, + ]; + $whereConditions = [ + 'id = ?' => $idMM, + 'AND (user_id = ?' => $user['id'], + 'OR is_shared = 1)', + ]; + + $isAdmin = api_is_platform_admin(); + + if ($user['status'] == SESSIONADMIN || $user['status'] == PLATFORM_ADMIN || $isAdmin) { + $whereConditions = [ + 'id = ?' => $idMM, + ]; + } + + Database::update($table, $params, $whereConditions); + + echo 'OK'; + } else { + echo 'KO'; + } + } else { + echo 'KO'; + } +} else { + echo 'KO'; +} diff --git a/plugin/mindmap/edit-mindmap/README.md b/plugin/mindmap/edit-mindmap/README.md new file mode 100644 index 000000000..8b9b1c734 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/README.md @@ -0,0 +1,13 @@ +mindMap +======= + +mindMap is a mind mapping application written entirely in JavaScript. I'm trying to use the latest HTML5 and CSS3 technologies in it. + +mindMap is hosted [here][1] and a simple document is [here][2]. + +[1]: http://kampfer.github.com/mindMap/client/index.html +[2]: http://kampfer.github.com/mindMap/client/help/introduce.html + +###Supported browsers + +Just chrome and firefox \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/app-base.js b/plugin/mindmap/edit-mindmap/app-base.js new file mode 100644 index 000000000..db7159cc7 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/app-base.js @@ -0,0 +1,88 @@ +setTimeout(function () { + + var mapData = { + 'document': [{ + 'id': '937205e4-c2b0-ead3-2db4-66719b3612e2', + 'parent': null, + 'children': null, + 'content': 'Chamilo', + 'offset': {'x': 360, 'y': 394} + }], 'lastModified': 1592229869766 + } + if (dataMMLoad != '') { + mapData = dataMMLoad + } + var view = document.getElementById('map-container') + view = kampfer.mindMap.window + var command = new kampfer.mindMap.command.CreateNewMap(mapData, view) + command.execute() + + var ht = '
  • ' + ht += 'Save
  • ' + $('#main-menu').html(ht) + +}, 100) + +var onlyOneUpdate = true + +setTimeout(function () { + saveMapProcess(false) +}, 6000) + +function saveMapProcess (redir) { + + if (onlyOneUpdate == false) {return false} + + if (redir) { + var ht = 'Save' + $('#mindmapmenu').html(ht) + } + + var map = kampfer.mindMap.mapManager.getMapData() + var mapString = JSON.stringify(map) + + var formData = {datamap: mapString} + + $.ajax({ + url: '../ajax/mindmap.ajax.php?id=' + idMM, + type: 'POST', data: formData, + success: function (data, textStatus, jqXHR) { + + onlyOneUpdate = true + + if (data.indexOf('KO') == -1) { + + if (redir) { + window.location.href = '../list.php?cid=' + MMGetParamValue('cid') + '&sid=' + MMGetParamValue('sid') + } + + } else { + alert('Error !') + } + + }, + error: function (jqXHR, textStatus, errorThrown) { + + alert('Error : ' + textStatus) + onlyOneUpdate = true + } + + }) +} + +function MMGetParamValue (param) { + + var u = document.location.href + var reg = new RegExp('(\\?|&|^)' + param + '=(.*?)(&|$)') + matches = u.match(reg) + + if (matches == null) {return ''} + + var vari = matches[2] != undefined ? decodeURIComponent(matches[2]).replace(/\+/g, ' ') : '' + + for (var i = 100; i > -1; i--) { + vari = vari.replace('#page' + i, '') + } + return vari + +} diff --git a/plugin/mindmap/edit-mindmap/build/config.json b/plugin/mindmap/edit-mindmap/build/config.json new file mode 100644 index 000000000..f551cb63a --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/config.json @@ -0,0 +1,8 @@ +{ + "name" : "kampfer", + "jsDir" : "client/js/", + "combine" : { + "source" : "", + "export" : "" + } +} \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/build/lib/combine.js b/plugin/mindmap/edit-mindmap/build/lib/combine.js new file mode 100644 index 000000000..1351caad3 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/lib/combine.js @@ -0,0 +1,75 @@ +var path = require('path'), + fs = require('fs'); + +var //项目名 + prefix, + //依赖关系 + dep, + //项目路径 + jsDir; + +var sort = function(src) { + if(!dep) { + console.log('Please call combine.init method first!'); + return; + } + + var scripts = [], + visited = {}, + visit = function(node) { + if(node in visited) { + return; + } + visited[node] = true; + + var requires = dep.getRequiresByPath(node); + + for(var require in requires) { + visit( dep.getPathByName(require) ); + } + + scripts.push(node); + }; + + visit(src); + + return scripts; +}; + +var deleteRequire = function(code) { + var reg = prefix + '\\.require\\([\'\"]([-_\\.a-zA-Z0-9]+)[\'\"]\\);[\n\r]*'; + reg = new RegExp(reg, 'g'); + return code.replace(reg, ''); +}; + +exports.init = function(dependency, config) { + dep = dependency; + jsDir = config.getJsDir(); + prefix = config.getProjectName(); +}; + +exports.combineCode = function(src) { + if(!src) { + console.log('Please input a file'); + return; + } + + src = path.relative(jsDir, src).replace(/\\/g, '/'); + var scripts = sort(src), code = []; + + for(var i = 0, l = scripts.length; i < l; i++) { + src = path.join(jsDir, scripts[i]); + var content = fs.readFileSync(src).toString(); + code.push( deleteRequire(content) ); + } + + return code.join('\n\n'); +}; + +exports.combineFile = function(src, output) { + var content = exports.combineCode(src); + fs.writeFileSync(output, content); + console.log('success! -> Combined File : ' + src); +}; + +exports.combine = function() {}; \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/build/lib/config.js b/plugin/mindmap/edit-mindmap/build/lib/config.js new file mode 100644 index 000000000..cb8474734 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/lib/config.js @@ -0,0 +1,19 @@ +var fs = require('fs'), + path = require('path'), + config = {}; + +exports.getProjectName = function() { + return config.name; +}; + +//js文件夹必须和tools在同一级目录 +exports.getJsDir = function() { + if(config.jsDir) { + return path.resolve(__dirname, '../../', config.jsDir); + } +}; + +exports.parse = function(uri) { + uri = path.normalize(uri); + config = JSON.parse( fs.readFileSync(uri) ); +}; \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/build/lib/dependency.js b/plugin/mindmap/edit-mindmap/build/lib/dependency.js new file mode 100644 index 000000000..788bc342a --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/lib/dependency.js @@ -0,0 +1,136 @@ +var fs = require('fs'), + path = require('path'); + +var //项目名 + prefix, + //项目路径 + jsDir, + //依赖文件路径 + depsPath, + //依赖关系缓存 + dependencies = { + pathToNames: {}, // 1 to many + nameToPath: {}, // 1 to 1 + requires: {} // 1 to many + }; + +function addDepsToFile(src, provides, requires) { + var insertText, fd; + + requires = '[' + requires.join(',') + ']'; + provides = '[' + provides.join(',') + ']'; + + insertText = [ + prefix, + '.addDependency(\'', + src, + '\', ', + provides, + ', ', + requires, + ');\n' + ].join(''); + + try{ + fd = fs.openSync(depsPath, 'a'); + fs.writeSync(fd, insertText, 0, 'utf8'); + }catch(e){ + return false; + } + + return true; +} + +function addDepsToObj(src, provides, requires) { + var provide, require, deps = dependencies; + + for( var i = 0; (provide = provides[i]); i++) { + deps.nameToPath[provide] = src; + if (!(src in deps.pathToNames)) { + deps.pathToNames[src] = {}; + } + deps.pathToNames[src][provide] = true; + } + + for( var j = 0; (require = requires[j]); j++) { + if (!(src in deps.requires)) { + deps.requires[src] = {}; + } + deps.requires[src][require] = true; + } +} + +function scanFile(uri) { + if( path.basename(uri) === 'deps.js') { + return; + } + + var reg = prefix + '\\.(require|provide)\\([\'\"]([-_\\.a-zA-Z0-9]+)[\'\"]\\);[\n\r]*', + provides = [], + requires = [], + content = fs.readFileSync(uri), + match; + + reg = new RegExp(reg, 'g'); + uri = path.relative(jsDir, uri).replace(/\\/g, '/'); + + do { + match = reg.exec(content); + + if(match) { + if( match[1] === 'require' ) { + requires.push('\'' + match[2] + '\''); + } + if( match[1] === 'provide' ) { + //prefix不应该被包含在provide name中 + var provide = match[2].split('.'); + if(provide[0] === prefix) { + provide = provide.slice(1).join('.'); + } else { + provide = match[2]; + } + + provides.push('\'' + provide + '\''); + } + } + } while(match); + + addDepsToFile( uri, provides, requires ); + addDepsToObj( uri, provides, requires ); + console.log('success! -> create one dependency record : ' + uri); +} + +exports.scan = function(uri) { + var stat = fs.statSync(uri); + + if( stat.isDirectory() ) { + fs.readdirSync(uri).forEach(function(part) { + exports.scan( path.join(uri, part) ); + }); + } else if( stat.isFile() ) { + scanFile(uri); + } +}; + +exports.init = function(config) { + prefix = config.getProjectName(); + jsDir = config.getJsDir(); + depsPath = path.join(jsDir, 'deps.js'); + + fs.writeFile(depsPath, ''); + console.log('success! -> create a new deps.js'); + + exports.scan(jsDir); +}; + +exports.getRequiresByPath = function(src) { + return dependencies.requires[src]; +}; + +exports.getPathByName = function(name) { + return dependencies.nameToPath[name]; +}; + +exports.getDepsPath = function() { + return depsPath; +}; \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/build/make.js b/plugin/mindmap/edit-mindmap/build/make.js new file mode 100644 index 000000000..f4565ddd1 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/make.js @@ -0,0 +1,16 @@ +var combine = require('./lib/combine'), + dep = require('./lib/dependency'), + fs = require('fs'), + path = require('path'), + config = require('./lib/config'), + UglifyJS = require("uglify-js"); + +config.parse( path.join(__dirname, 'config.json') ); +dep.init(config); +combine.init(dep, config); + +var testUri = path.resolve(__dirname, '../', 'client/js/mindmap.js'), + testOutput = path.resolve(__dirname, '../', 'client/js/mindmap.min.js'); + +combine.combineFile(testUri, testOutput); +fs.writeFileSync(testOutput, UglifyJS.minify(testOutput).code); \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/build/makeDeps.js b/plugin/mindmap/edit-mindmap/build/makeDeps.js new file mode 100644 index 000000000..8907a0d89 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/build/makeDeps.js @@ -0,0 +1,7 @@ +var dep = require('./lib/dependency'), + fs = require('fs'), + path = require('path'), + config = require('./lib/config'); + +config.parse( path.join(__dirname, 'config.json') ); +dep.init(config); \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/doc/code-structure.md b/plugin/mindmap/edit-mindmap/doc/code-structure.md new file mode 100644 index 000000000..442b46ddb --- /dev/null +++ b/plugin/mindmap/edit-mindmap/doc/code-structure.md @@ -0,0 +1,31 @@ +# Code Structure Of Mindmap # + +## Model ## + +mapmanager.js + +------------- + +## View ## + +window.js + +toolbar.js + +menu.js + +map.js + +node.js + +caption.js + +branch.js + +----------- + +## Controller ## + +command.js + +app.js \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/doc/note.md b/plugin/mindmap/edit-mindmap/doc/note.md new file mode 100644 index 000000000..136984f1e --- /dev/null +++ b/plugin/mindmap/edit-mindmap/doc/note.md @@ -0,0 +1,31 @@ +关于架构设计 +============ + +设计一个领域实体,必须明确这个领域实体必须知道其他哪些领域实体(帮助其完成业务),之后对于其他领域实体可以完全无视。 + +程序的启动程序的主要工作是:初始化view、model和controller。model比较封闭,初始化时一般不需要外部帮助;view初始化一般需要model数据等信息;controller需要view和model的引用,保证view和model信息的同步。 + +数据的流动方向和路径? + +启动文件负责配置好所有组件,并在命名空间中保存引用. +所有view组件注册在mindMap.view下, 所有modal注册在mindMap.modal下, controller类似. +组件需要调用其它组件时, 可以将mindMap作为参数直接传递. + + +已经使用的设计模式 +================== + +1. Observer +2. Composition +3. Command +4. Template Method + + +**Composition模式** +类有两部分的功能: 管理子节点和递归操作 + +**Decorator模式** +https://gist.github.com/kampfer/5012413 + +**Abstract Factory/Factory Method** +抽象工厂模式依赖工厂方法模式 他们也经常和模板方法模式一起使用 \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/img/mindmap.png b/plugin/mindmap/edit-mindmap/img/mindmap.png new file mode 100644 index 0000000000000000000000000000000000000000..f6cf34263d0f639630ca61e2250da20f1f3a02a5 GIT binary patch literal 3261 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 vKmNA!K4bTi&}<>T&**Yg-HQ`ieFArT*E8t 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/img/mindmap128gray.png b/plugin/mindmap/edit-mindmap/img/mindmap128gray.png new file mode 100644 index 0000000000000000000000000000000000000000..2f02b8bcdb1e53fa6235fb01a775da612010139f GIT binary patch literal 3551 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/index.php b/plugin/mindmap/edit-mindmap/index.php new file mode 100644 index 000000000..2725cca79 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/index.php @@ -0,0 +1,134 @@ + + + + + MindMap + + + + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + + + diff --git a/plugin/mindmap/edit-mindmap/mindMap.js b/plugin/mindmap/edit-mindmap/mindMap.js new file mode 100644 index 000000000..efe40cc44 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/mindMap.js @@ -0,0 +1,11 @@ +var connect = require('connect'); +var path = require('path'); + +connect.static.mime.define({ + 'text/cache-manifest' : ['mf', 'manifest'] +}); + +var app = connect() + .use( connect.logger('dev') ) + .use( connect.static('client/') ) + .listen(8000); \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/mindMap.txt b/plugin/mindmap/edit-mindmap/mindMap.txt new file mode 100644 index 000000000..f7a3fdc1c --- /dev/null +++ b/plugin/mindmap/edit-mindmap/mindMap.txt @@ -0,0 +1,52 @@ +{ + "tree" : { + "id" : "map", + "children" : [ + { + "id" : "child1", + "content" : "child1", + "offset" : {"x" : 100, "y" : 200}, + "parent" : "map", + "children" : [ + { + "id" : "child1-1", + "content" : "child1-1", + "offset" : {"x" : 100, "y" : 200}, + "parent" : "child1", + "children" : [ + { + "id" : "child1-1-1", + "content" : "child1-1-1", + "offset" : {"x" : 200, "y" : 200}, + "parent" : "child1-1", + "children" : null + } + ] + }, + { + "id" : "child1-2", + "content" : "child1-2", + "offset" : {"x" : 200, "y" : 200}, + "parent" : "child1", + "children" : null + } + ] + }, + { + "id" : "child2", + "content" : "child2", + "offset" : {"x" : 100, "y" : 100}, + "parent" : "map", + "children" : null + }, + { + "id" : "child3", + "content" : "child3", + "offset" : {"x" : 200, "y" : 100}, + "parent" : "map", + "children" : null + } + ] + }, + "name" : "untitled" +} \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/css/DemoTransformsAnimations.css b/plugin/mindmap/edit-mindmap/vendor/css/DemoTransformsAnimations.css new file mode 100644 index 000000000..359a40da2 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/DemoTransformsAnimations.css @@ -0,0 +1,216 @@ +/*Styles used for the various demonstration divs that need to be vendor prefixed*/ +.TransformDemoPerspective200 { + -ms-perspective: 200px; +} + +.TransformDemoPerspectiveOrigin3050 { + -ms-perspective-origin: 30% 50%; +} + +.TransformDemoPerspectiveOrigin8050 { + -ms-perspective-origin: 80% 50%; +} + +.TransformDemoDivRotate45 { +} + +.TransformDemoDivRotate45:hover { + -ms-transform: rotate(45deg); +} + +.TransformDemoDivSkewX45:hover { + -ms-transform: skewX(45deg); +} + +.Transform3DDemoDivRotateX:hover { + -ms-transform: rotateX(45deg); +} + +.Transform3DDemoDivRotateY:hover { + -ms-transform: rotateY(45deg); +} + +.Transform3DDemoDivTranslateZ:hover { + -ms-transform: translateZ(-50px); +} + +.Transform3DDemoDivRotateYTranslateZ:hover { + -ms-transform: rotateY(45deg) translateZ(-50px); +} +.Transform3DDemoDivTranslateZRotateY:hover { + -ms-transform: translateZ(-50px) rotateY(45deg); +} + +@-ms-keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@-ms-keyframes fadeOutInOut { + 0% { + opacity: 1; + } + 25% { + opacity: 0; + } + 75% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +@-ms-keyframes fadeOutInOutSwirl { + 0% { + opacity: 1; + } + 25% { + opacity: 0.5; + -ms-transform: rotateZ(0deg); + } + 75% { + opacity: 0.5; + -ms-transform: rotateZ(180deg); + } + 100% { + opacity: 1.0; + -ms-transform: rotateZ(360deg); + } +} + + +@-ms-keyframes fullPageInOut { + 50% { + opacity: 0.5; + -ms-transform: scale(0.1) rotateZ(360deg); + } +} + +.TransformDemoDivFadeOut:hover { + -ms-animation-duration: 2s; + -ms-animation-name: fadeOut; +} + +.TransformDemoDivFadeOutInOut:hover { + -ms-animation-duration: 2s; + -ms-animation-name: fadeOutInOut; +} + +.TransformDemoDivFadeOutInOutSwirl:hover { + -ms-animation-duration: 2s; + -ms-animation-name: fadeOutInOutSwirl; +} + +.TransformTranslateX400px { + -ms-transform: translateX(400px); +} + +.TransformTranslateY300px { + -ms-transform: translateY(300px); +} + +.TransformRotate45 { + -ms-transform: rotate(45deg); +} + +.TransformRotateMinus35 { + -ms-transform: rotate(-35deg); +} + +.TransformSkewX25 { + -ms-transform: skewX(25deg); +} + +.TransformSkewY25 { + -ms-transform: skewY(25deg); +} + +.TransformScale075 { + -ms-transform: scale(0.75); +} + +.TransformScaleX075 { + -ms-transform: scaleX(0.75); +} + +.TransformScaleY075 { + -ms-transform: scaleY(0.75); +} + +.TransformScaleX125 { + -ms-transform: scaleX(1.25); +} + +.TransformScaleY125 { + -ms-transform: scaleY(1.25); +} + +.TransformRotate45TranslateX200px { + -ms-transform: rotate(45deg) translateX(200px); +} + +.TransformTranslateX200pxRotate45 { + -ms-transform: translateX(200px) rotate(45deg); +} + +.TransformOrigin0050 { + -ms-transform-origin: 0% 50%; +} + +.TransformOrigin0000 { + -ms-transform-origin: 0% 0%; +} + +.TransformOrigin00100 { + -ms-transform-origin: 0% 100%; +} + +.TransformOrigin100100 { + -ms-transform-origin: 100% 100%; +} + +.TransformOrigin2525 { + -ms-transform-origin: 25% 25%; +} + +.TransformOrigin5000 { + -ms-transform-origin: 50% 0%; +} + +.TransformOrigin50100 { + -ms-transform-origin: 50% 100%; +} + +.TransformOrigin6666 { + -ms-transform-origin: 66% 66%; +} + +.AnimationTimingEase { + -ms-animation-timing-function: ease; +} + +.AnimationTimingEaseIn { + -ms-animation-timing-function: ease-in; +} + +.AnimationTimingEaseOut { + -ms-animation-timing-function: ease-out; +} + +.AnimationTimingEaseInOut { + -ms-animation-timing-function: ease-in-out; +} + +.AnimationTimingLinear { + -ms-animation-timing-function: linear; +} + +.Animation3Iterations { + -ms-animation-iteration-count: 3; +} + diff --git a/plugin/mindmap/edit-mindmap/vendor/css/FullPageAnimationsPrefixed.css b/plugin/mindmap/edit-mindmap/vendor/css/FullPageAnimationsPrefixed.css new file mode 100644 index 000000000..6b0f18cb9 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/FullPageAnimationsPrefixed.css @@ -0,0 +1,223 @@ +/* Copyright © Microsoft Corporation. All Rights Reserved. */ +/* Demo Author: Charilaos "Harris" Papadopoulos, Microsoft Corporation */ + + +/* + Predefined full-page animations for the CSS Animation/Transform IE Test Drive Demo + Please check FullPageAnimations.js for more information +*/ + + +@-ms-keyframes rotateInLeft { + + from { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateY(180deg); + } + + to { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateY(0deg); + } +} + +@-ms-keyframes rotateOutLeft { + + from { + -ms-transform-origin: 0% 0%; + } + + to { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateY(180deg); + } +} + +@-ms-keyframes rotateInRight { + + from { + -ms-transform-origin: 100% 0%; + -ms-transform: rotateY(-180deg); + + } + + to { + -ms-transform-origin: 100% 0%; + } +} + +@-ms-keyframes rotateInRightWithoutToKeyframe { + + from { + -ms-transform-origin: 100% 0%; + -ms-transform: rotateY(-180deg); + } +} + +@-ms-keyframes rotateOutRight { + + from { + -ms-transform-origin: 100% 0%; + } + + to { + -ms-transform-origin: 100% 0%; + -ms-transform: rotateY(-180deg); + } +} + +@-ms-keyframes fadeIn { + from { + opacity: 0; + } +} + +@-ms-keyframes fadeOut { + to { + opacity: 0; + } +} + +@-ms-keyframes whirlIn { + from { + -ms-transform-origin: 50% 50%; + -ms-transform: scale(0) rotateZ(1260deg); + } + + to { + -ms-transform-origin: 50% 50%; + } + +} + +@-ms-keyframes whirlOut { + from { + -ms-transform-origin: 50% 50%; + } + + to { + -ms-transform-origin: 50% 50%; + -ms-transform: scale(0) rotateZ(1260deg); + } + +} + +@-ms-keyframes fallFromTop { + from { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateX(-180deg); + animation-timing-function: ease; + } + + 50% { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateX(30deg); + animation-timing-function: ease; + } + + 85% { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateX(-10deg); + animation-timing-function: ease; + } + + to { + -ms-transform-origin: 0% 0%; + -ms-transform: rotateX(0deg); + animation-timing-function: ease; + } +} + +@-ms-keyframes dropToBottom { + from { + -ms-transform-origin: 0% 100%; + } + + to { + -ms-transform-origin: 0% 100%; + -ms-transform: rotateX(180deg); + } +} + +@-ms-keyframes slideInSkew { + from { + -ms-transform-origin: 0% 100%; + -ms-transform: translateX(-200%) skewX(-45deg); + } + + 50% { + -ms-transform-origin: 0% 100%; + -ms-transform: translateX(0%) skewX(-45deg); + } + + 90% { + -ms-transform-origin: 0% 100%; + -ms-transform: translateX(0%) skewX(10deg); + } + + to { + -ms-transform-origin: 0% 100%; + -ms-transform:translateX(0%) skewX(0deg); + } +} + +@-ms-keyframes slideOutSkew { + from { + -ms-transform-origin: 0% 100%; + -ms-transform: translateX(0%); + } + + to { + -ms-transform-origin: 0% 100%; + -ms-transform: skewX(-45deg) translateX(200%) ; + } +} + +@-ms-keyframes tumbleIn { + from { + -ms-transform-origin: 0% 100%; + -ms-transform: rotateZ(-180deg); + } + + to { + -ms-transform-origin: 0% 100%; + } + +} + +@-ms-keyframes tumbleOut { + from { + -ms-transform-origin: 100% 100%; + } + + to { + -ms-transform-origin: 100% 100%; + -ms-transform: rotateZ(180deg); + } + +} + +@-ms-keyframes expandIn { + from { + -ms-transform: scale(0); + } + + 50% { + + -ms-transform: scale(1); + } + + 75% { + -ms-transform: scale(1.2); + } + + to { + -ms-transform: scale(1); + } +} + +@-ms-keyframes collapseOut { + to { + -ms-transform: scale(0); + } +} \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/css/app.css b/plugin/mindmap/edit-mindmap/vendor/css/app.css new file mode 100644 index 000000000..27b1df845 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/app.css @@ -0,0 +1,174 @@ +/* app css start */ +html, body, .map-container{ + width:100%; + height:100%; +} + +.dropdown-menu .short-cut{ + float: right; + font-size: 12px; +} + +.app-tool-bar{ + position: fixed; + top: 10px; + left: 10px; + z-index: 9999; +} + +.app-tool-bar .dropdown-menu{ + min-width: 250px; +} + +.app-tool-bar > .dropdown-menu{ + display: block; + min-width: 40px; +} + +.app-tool-bar .dropdown-menu > li > a{ + padding: 5px 10px; +} + +.app-tool-bar .dropdown-submenu > a:after{ + margin-right: -5px; + margin-top: 3px; +} + +.app-container { + position: relative; + width:100%; + height:100%; + overflow: hidden; +} + +.map{ + width: 2000px; + height: 2000px; + background:url('../img/bg.gif') repeat; +} + +.app-footer { + position: fixed; + left: 0px; + bottom: 0px; + width: 100%; + height: 40px; + line-height: 40px; + background-color: #000; + text-align: center; + z-index: 9999; +} + +.app-footer:after{ + clear: both; +} + +.app-footer a{ + color: #333; +} + +.app-footer a:hover{ + text-decoration: none; + color: #fff; +} + +.app-footer .brand{ + color: #000000; + font-weight: bold; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 30px rgba(255, 255, 255, 0.125); + -webkit-transition: all .2s linear; + -moz-transition: all .2s linear; + transition: all .2s linear; + font-size: 20px; + position: absolute; + right: 90px; + top: 0px; +} + +div.node{ + position: absolute; + cursor: default; +} + +div.node-caption{ + min-width: 100px; + max-width: 300px; + padding:10px; + z-index: 99; + border: 1px solid rgba(0, 0, 0, 0.4); + border-radius : 6px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: black; + color: white; + position: absolute; +} + +div.node-caption textarea{ + margin: 0; +} + +div.node-caption.black{ + border-color: #222 #222 black; + background-color: #363636; + background-image: -webkit-linear-gradient(top, #444, #222); + background-repeat: repeat-x; +} + +div.node-caption.red{ + border-color: #BD362F #BD362F #802420; + background-color: #DA4F49; + background-image: -webkit-linear-gradient(top, #EE5F5B, #BD362F); + background-repeat: repeat-x; +} + +div.node-caption.orange{ + border-color: #F89406 #F89406 #AD6704; + background-color: #FAA732; + background-image: -webkit-linear-gradient(top, #FBB450, #F89406); + background-repeat: repeat-x; +} + +div.node-caption.green{ + border-color: #51A351 #51A351 #387038; + background-color: #5BB75B; + background-image: -webkit-linear-gradient(top, #62C462, #51A351); + background-repeat: repeat-x; +} + +div.node-caption.blue{ + border-color: #2F96B4 #2F96B4 #1F6377; + background-color: #2F96B4; + background-image: -webkit-linear-gradient(top, #5BC0DE, #2F96B4); + background-image: -moz-linear-gradient(top, #5BC0DE, #2F96B4); + background-repeat: repeat-x; +} + +div.node-caption.dark-blue{ + border-color: #04C #04C #002A80; + background-color: #04C; + background-image: -webkit-linear-gradient(top, #08C, #04C); + background-repeat: repeat-x; +} + +div.node-caption:hover{ + background-position: 0 -15px; + transition: background-position 0.1s linear 0s; +} + +div.node-caption.blue:hover{ + background-color: #2F96B4; +} + +div.node canvas{ + position:absolute; +} + +.modal .app-file-list{ + max-height: 200px; + overflow: auto; +} + +.modal .app-file-name{ + padding-top: 20px; +} \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.css b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.css new file mode 100644 index 000000000..a3352d774 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.css @@ -0,0 +1,1092 @@ +/*! + * Bootstrap Responsive v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +@-ms-viewport { + width: device-width; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} + +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .dropdown-menu a:hover { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:hover { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.min.css b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.min.css new file mode 100644 index 000000000..5cb833ff0 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap-responsive.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.css b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.css new file mode 100644 index 000000000..8ab3cefcf --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.css @@ -0,0 +1,6039 @@ +/*! + * Bootstrap v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} + +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-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]: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; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + width: 940px; +} + +.span11 { + width: 860px; +} + +.span10 { + width: 780px; +} + +.span9 { + width: 700px; +} + +.span8 { + width: 620px; +} + +.span7 { + width: 540px; +} + +.span6 { + width: 460px; +} + +.span5 { + width: 380px; +} + +.span4 { + width: 300px; +} + +.span3 { + width: 220px; +} + +.span2 { + width: 140px; +} + +.span1 { + width: 60px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} + +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} + +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} + +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} + +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} + +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} + +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} + +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} + +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} + +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} + +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} + +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} + +.container:before, +.container:after { + display: table; + line-height: 0; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} + +.container-fluid:before, +.container-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} + +small { + font-size: 85%; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +cite { + font-style: normal; +} + +.muted { + color: #999999; +} + +a.muted:hover { + color: #808080; +} + +.text-warning { + color: #c09853; +} + +a.text-warning:hover { + color: #a47e3c; +} + +.text-error { + color: #b94a48; +} + +a.text-error:hover { + color: #953b39; +} + +.text-info { + color: #3a87ad; +} + +a.text-info:hover { + color: #2d6987; +} + +.text-success { + color: #468847; +} + +a.text-success:hover { + color: #356635; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + line-height: 40px; +} + +h1 { + font-size: 38.5px; +} + +h2 { + font-size: 31.5px; +} + +h3 { + font-size: 24.5px; +} + +h4 { + font-size: 17.5px; +} + +h5 { + font-size: 14px; +} + +h6 { + font-size: 11.9px; +} + +h1 small { + font-size: 24.5px; +} + +h2 small { + font-size: 17.5px; +} + +h3 small { + font-size: 14px; +} + +h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} + +li { + line-height: 20px; +} + +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} + +ul.inline > li, +ol.inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 10px; +} + +.dl-horizontal { + *zoom: 1; +} + +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + line-height: 0; + content: ""; +} + +.dl-horizontal:after { + clear: both; +} + +.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; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 25px; +} + +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} + +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code { + padding: 2px 4px; + color: #d14; + white-space: nowrap; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; +} + +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +select[multiple], +select[size] { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} + +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} + +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} + +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} + +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} + +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} + +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} + +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} + +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} + +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} + +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} + +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} + +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} + +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} + +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} + +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} + +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} + +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} + +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} + +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + margin-bottom: 5px; + font-size: 0; + white-space: nowrap; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls:first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child { + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover tbody tr:hover td, +.table-hover tbody tr:hover th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success td { + background-color: #dff0d8; +} + +.table tbody tr.error td { + background-color: #f2dede; +} + +.table tbody tr.warning td { + background-color: #fcf8e3; +} + +.table tbody tr.info td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover td { + background-color: #c4e3f3; +} + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +/* White icons with optional class, or on hover/active states of certain elements */ + +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.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; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu li > a:hover, +.dropdown-menu li > a:focus, +.dropdown-submenu:hover > a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .active > a, +.dropdown-menu .active > a:hover { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu .disabled > a, +.dropdown-menu .disabled > a:hover { + color: #999999; +} + +.dropdown-menu .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open { + *z-index: 1000; +} + +.open > .dropdown-menu { + display: block; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu > a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +.collapse.in { + height: auto; +} + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #bbbbbb; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #a2a2a2; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +.btn-mini { + padding: 0 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn { + border-color: #c5c5c5; + border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover { + color: #333333; + text-decoration: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group + .btn-group { + margin-left: 5px; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group > .btn + .btn { + margin-left: -1px; +} + +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 14px; +} + +.btn-group > .btn-mini { + font-size: 10.5px; +} + +.btn-group > .btn-small { + font-size: 11.9px; +} + +.btn-group > .btn-large { + font-size: 17.5px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + *padding-top: 5px; + padding-right: 8px; + *padding-bottom: 5px; + padding-left: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group > .btn-mini + .dropdown-toggle { + *padding-top: 2px; + padding-right: 5px; + *padding-bottom: 2px; + padding-left: 5px; +} + +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} + +.btn-group > .btn-large + .dropdown-toggle { + *padding-top: 7px; + padding-right: 12px; + *padding-bottom: 7px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} + +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} + +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} + +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} + +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + margin-top: 8px; + margin-left: 0; +} + +.btn-mini .caret, +.btn-small .caret, +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical > .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert, +.alert h4 { + color: #c09853; +} + +.alert h4 { + margin: 0; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success h4 { + color: #468847; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info h4 { + color: #3a87ad; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav > li > a { + display: block; +} + +.nav > li > a:hover { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li > a > img { + max-width: none; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li + .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list > li > a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list > li > a { + padding: 3px 15px; +} + +.nav-list > .active > a, +.nav-list > .active > a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs > li, +.nav-pills > li { + float: left; +} + +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + margin-bottom: -1px; +} + +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover { + color: #ffffff; + background-color: #0088cc; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.nav .dropdown-toggle .caret { + margin-top: 6px; + border-top-color: #0088cc; + border-bottom-color: #0088cc; +} + +.nav .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav > .dropdown.active > a:hover { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open > a:hover { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below > .nav-tabs > li > a:hover { + border-top-color: #ddd; + border-bottom-color: transparent; +} + +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover { + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav > .disabled > a { + color: #999999; +} + +.nav > .disabled > a:hover { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; +} + +.navbar-inner { + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover { + text-decoration: none; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav > li { + float: left; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav > li > .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav > li > .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown > a:hover .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; +} + +.navbar-inverse .brand { + color: #999999; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul > li { + display: inline; +} + +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul > li > a:hover, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} + +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} + +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li > a:hover { + text-decoration: none; + background-color: #f5f5f5; +} + +.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 > span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.fade { + top: -25%; + -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; + -moz-transition: opacity 0.3s linear, top 0.3s ease-out; + -o-transition: opacity 0.3s linear, top 0.3s ease-out; + transition: opacity 0.3s linear, top 0.3s ease-out; +} + +.modal.fade.in { + top: 10%; +} + +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} + +.modal-header .close { + margin-top: 2px; +} + +.modal-header h3 { + margin: 0; + line-height: 30px; +} + +.modal-body { + position: relative; + max-height: 400px; + padding: 15px; + overflow-y: auto; +} + +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + line-height: 0; + content: ""; +} + +.modal-footer:after { + clear: both; +} + +.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; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + margin-top: -3px; +} + +.tooltip.right { + margin-left: 3px; +} + +.tooltip.bottom { + margin-top: 3px; +} + +.tooltip.left { + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + 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-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + width: 236px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.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; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + 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 { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + 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, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; +} + +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before, +.thumbnails:after { + display: table; + line-height: 0; + content: ""; +} + +.thumbnails:after { + clear: both; +} + +.row-fluid .thumbnails { + margin-left: 0; +} + +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} + +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +a.thumbnail:hover { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail > img { + display: block; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #555555; +} + +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media .pull-left { + margin-right: 10px; +} + +.media .pull-right { + margin-left: 10px; +} + +.media-list { + margin-left: 0; + list-style: none; +} + +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; +} + +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.badge { + padding-right: 9px; + padding-left: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} + +.label:empty, +.badge:empty { + display: none; +} + +a.label:hover, +a.badge:hover { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label-important, +.badge-important { + background-color: #b94a48; +} + +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} + +.label-warning, +.badge-warning { + background-color: #f89406; +} + +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} + +.label-success, +.badge-success { + background-color: #468847; +} + +.label-success[href], +.badge-success[href] { + background-color: #356635; +} + +.label-info, +.badge-info { + background-color: #3a87ad; +} + +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} + +.label-inverse, +.badge-inverse { + background-color: #333333; +} + +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} + +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} + +.btn-mini .label, +.btn-mini .badge { + top: 0; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 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: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} + +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} + +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion { + margin-bottom: 20px; +} + +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading { + border-bottom: 0; +} + +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +.accordion-toggle { + cursor: pointer; +} + +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img { + display: block; + line-height: 1; +} + +.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: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right { + right: 15px; + left: auto; +} + +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + color: inherit; +} + +.hero-unit li { + line-height: 30px; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff --git a/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.min.css b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.min.css new file mode 100644 index 000000000..140f731df --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v2.2.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}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-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]: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}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover{color:#808080}.text-warning{color:#c09853}a.text-warning:hover{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover{color:#2d6987}.text-success{color:#468847}a.text-success:hover{color:#356635}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.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}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.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;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #bbb;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret{border-top-color:#555;border-bottom-color:#555}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover{text-decoration:none;background-color:#f5f5f5}.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>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.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}.tooltip{position:absolute;z-index:1030;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;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-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.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;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;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{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;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,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media .pull-left{margin-right:10px}.media .pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 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:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img{display:block;line-height:1}.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:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/plugin/mindmap/edit-mindmap/vendor/css/help.css b/plugin/mindmap/edit-mindmap/vendor/css/help.css new file mode 100644 index 000000000..3523085e8 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/help.css @@ -0,0 +1,6 @@ +html,body{margin:0;padding:0;} +html{background-color: rgb(0,135,255);height:100%;} +body{background-color: white;min-height:100%;} +.contentWrapper{width:940px;margin:0 auto;overflow: hidden;} +.nav{padding:30px 0;} +.next-page{float:right;} \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/css/qunit.css b/plugin/mindmap/edit-mindmap/vendor/css/qunit.css new file mode 100644 index 000000000..a1dd6a809 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/css/qunit.css @@ -0,0 +1,236 @@ +/** + * QUnit v1.7.0pre - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2012 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; +} + +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { + margin: 0; + padding: 0; +} + + +/** Header */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #8699a4; + background-color: #0d3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: normal; + + border-radius: 15px 15px 0 0; + -moz-border-radius: 15px 15px 0 0; + -webkit-border-top-right-radius: 15px; + -webkit-border-top-left-radius: 15px; +} + +#qunit-header a { + text-decoration: none; + color: #c2ccd1; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #fff; +} + +#qunit-header label { + display: inline-block; + padding-left: 0.5em; +} + +#qunit-banner { + height: 5px; +} + +#qunit-testrunner-toolbar { + padding: 0.5em 0 0.5em 2em; + color: #5E740B; + background-color: #eee; +} + +#qunit-userAgent { + padding: 0.5em 0 0.5em 2.5em; + background-color: #2b81af; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 0.5em 0.4em 2.5em; + border-bottom: 1px solid #fff; + list-style-position: inside; +} + +#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { + display: none; +} + +#qunit-tests li strong { + cursor: pointer; +} + +#qunit-tests li a { + padding: 0.5em; + color: #c2ccd1; + text-decoration: none; +} +#qunit-tests li a:hover, +#qunit-tests li a:focus { + color: #000; +} + +#qunit-tests ol { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #fff; + + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + + box-shadow: inset 0px 2px 13px #999; + -moz-box-shadow: inset 0px 2px 13px #999; + -webkit-box-shadow: inset 0px 2px 13px #999; +} + +#qunit-tests table { + border-collapse: collapse; + margin-top: .2em; +} + +#qunit-tests th { + text-align: right; + vertical-align: top; + padding: 0 .5em 0 0; +} + +#qunit-tests td { + vertical-align: top; +} + +#qunit-tests pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del { + background-color: #e0f2be; + color: #374e0c; + text-decoration: none; +} + +#qunit-tests ins { + background-color: #ffcaca; + color: #500; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: black; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + margin: 0.5em; + padding: 0.4em 0.5em 0.4em 0.5em; + background-color: #fff; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass { + color: #5E740B; + background-color: #fff; + border-left: 26px solid #C6E746; +} + +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } +#qunit-tests .pass .test-name { color: #366097; } + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests li li.fail { + color: #710909; + background-color: #fff; + border-left: 26px solid #EE5757; + white-space: pre; +} + +#qunit-tests > li:last-child { + border-radius: 0 0 15px 15px; + -moz-border-radius: 0 0 15px 15px; + -webkit-border-bottom-right-radius: 15px; + -webkit-border-bottom-left-radius: 15px; +} + +#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name { color: #000000; } + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: green; } + +#qunit-banner.qunit-fail { background-color: #EE5757; } + + +/** Result */ + +#qunit-testresult { + padding: 0.5em 0.5em 0.5em 2.5em; + + color: #2b81af; + background-color: #D2E0E6; + + border-bottom: 1px solid white; +} +#qunit-testresult .module-name { + font-weight: bold; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; + width: 1000px; + height: 1000px; +} diff --git a/plugin/mindmap/edit-mindmap/vendor/help/introduce.html b/plugin/mindmap/edit-mindmap/vendor/help/introduce.html new file mode 100644 index 000000000..1423f8e40 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/help/introduce.html @@ -0,0 +1,68 @@ + + + + + MindMap帮助文档 + + + + + +
    +

    MindMap帮助文档

    + +

    简介

    + +

    + MindMap是一款web版的思维导图应用,整个项目使用html+css+javascript开发。MindMap利用了许多html5特性:StorageFile AccessGraphicsPresentation。 +

    + +

    技术实现

    + +

    + MindMap以bootstrap为基础开发css+html。 +

    +

    + MindMap采用OOP的开发方式,使用类似backboneextend方法实现继承。许多设计模式被应用到MindMap中:使用Command模式实现操作的Undo和Redo功能,使用Compositon模式组织map和node对象等等。 +

    +

    + MindMap的js组织方式,变量命名方式受到google closure的影响。 MindMap实现了一个类似closure的js加载器,配合一个nodejs脚本(用于生成js文件依赖记录)来管理组件之间的依赖关系。 +

    + +

    兼容性

    + +

    + 目前mindMap支持:Firefox19、Chrome25。我没有条件测试Safari;而Opera最近宣布将加入webkit的大家庭,所以我也没有再花时间测试;MindMap不支持IE。 +

    + +

    功能特性

    + +
      +
    1. + 可以将map保存为本地的文本文件,也能打开本地文本格式的map +
    2. +
    3. + 可以将map保存在浏览器的storage中,也能打开在storage中的map +
    4. +
    5. + 创建新的map,为map添加节点,为节点添加子节点,删除节点。 +
    6. +
    7. + 改变节点的内容、位置 +
    8. +
    9. + 保存操作,实现了简单的redo和undo功能 +
    10. +
    11. + 节点的copy、cut、paste功能 +
    12. +
    + +
    + + \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/help/license.html b/plugin/mindmap/edit-mindmap/vendor/help/license.html new file mode 100644 index 000000000..ef1b1c0ce --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/help/license.html @@ -0,0 +1,46 @@ + + + + + templates + + + + + + +
    +

    mindMap帮助文档

    +
    + +

    简介

    + +

    MindMap是一款web版的思维导图应用,整个项目使用html+css+javascript开发。MindMap利用了许多html5特性:StorageFile AccessGraphicsPresentation。目前mindMap支持的浏览器有:Firefox19、Chrome25,我没有条件测试Safari,而Opera最近宣布将加入webkit的大家庭,所以我也没有再花时间测试,mindMap不支持IE。

    + +

    建立MindMap的目的是将自己在学习工作中积累的新知识和技巧应用到实践中。

    + +

    MindMap以bootstrap为基础开发css+html。 +js结构,命名方式类似于google closure,mindMap实现了一个类似closure的js加载器,并且使用一个nodejs脚本管理js文件的依赖关系。

    + +

    功能特性

    + +
      +
    1. 可以将map保存为本地的文本文件,也能本地文本格式的map
    2. +
    3. 可以将map保存在浏览器的storage中,也能打开在storage中的map
    4. +
    5. 创建新的map,为map添加节点,为节点添加子节点,删除节点。
    6. +
    7. 改变节点的内容、位置
    8. +
    9. 保存操作,实现了简单的redo和undo功能
    10. +
    11. 节点的copy、cut、paste功能
    12. +
    +
    +
    +
    + + \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/help/map.html b/plugin/mindmap/edit-mindmap/vendor/help/map.html new file mode 100644 index 000000000..30f90d679 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/help/map.html @@ -0,0 +1,103 @@ + + + + + + + MindMap帮助文档 + + + + + + +
    +

    MindMap帮助文档

    +

    map操作

    +
      +
    1. +

      + 创建新map:file-〉Create New Map 或者使用快捷键[ctrl+m] +

      + +

      create new map +

      +
    2. +
    3. +

      + 打开硬盘中的map:file-〉Open Map In Disk +

      + +

      open map in disk +

      +
    4. +
    5. +

      + 打开storage中的map:file-〉Open Map In Storage +

      + +

      open map in storage +

      +
    6. +
    7. +

      + 将map保存到硬盘中:file-〉Save Map In Disk 或者使用快捷键[ctrl+shift+s] +

      + +

      open map in storage +

      +
    8. +
    9. +

      + 将map保存到storage中:file-〉Save Map In Storage 或者使用快捷键[ctrl+s] +

      + +

      open map in storage +

      +
    10. +
    11. +

      + contextMenu: 右键点击map空白处 +

      +
    12. +
    13. +

      + 取消上一步操作:file-〉Undo 或者 contextMenu-〉Undo 或者使用快捷键[ctrl+z] +

      + +

      undo +

      + +

      undo +

      +
    14. +
    15. +

      + 还原上一步操作:file-〉Redo 或者 contextMenu-〉Redo 或者使用快捷键[ctrl+y] +

      + +

      redo +

      + +

      redo +

      +
    16. +
    17. +

      + 查看help文档 +

      + +

      redo +

      +
    18. +
    + +
    + + diff --git a/plugin/mindmap/edit-mindmap/vendor/help/node.html b/plugin/mindmap/edit-mindmap/vendor/help/node.html new file mode 100644 index 000000000..8c0e1f4ef --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/help/node.html @@ -0,0 +1,99 @@ + + + + + + + MindMap帮助文档 + + + + + + +
    +

    MindMap帮助文档

    +

    node操作

    +
      +
    1. +

      + 选中节点:左键单击节点 +

      +
    2. +
    3. +

      + nodeContextMenu:右键单击节点 +

      +
    4. +
    5. +

      + 修改节点位置:左键点击节点不松,拖动鼠标 +

      +
    6. +
    7. +

      + 修改节点内容:nodeContextMenu-〉edit +

      + +

      edit node +

      +
    8. +
    9. +

      + 创建根节点:contextMenu-〉Creat New Root Node +

      + +

      cteate root node +

      +
    10. +
    11. +

      + 添加子节点:nodeContextmenu-> Append Child Node +

      + +

      append child node +

      +
    12. +
    13. +

      + 删除节点:nodeContextMenu-〉Delete +

      + +

      delete node +

      +
    14. +
    15. +

      + 复制节点:选中节点-〉(点击右键-〉Copy)或者(file-〉Copy) 或者使用快捷键[ctrl+c] +

      + +

      copy node +

      +
    16. +
    17. +

      + 剪切节点:选中节点-〉(点击右键-〉cut)或者(file-〉cut) 或者使用快捷键[ctrl+x] +

      + +

      cut node +

      +
    18. +
    19. +

      + 粘贴节点:选中节点-〉(点击右键-〉Paste)或者(file-〉Paste) 或者使用快捷键[ctrl+v] +

      + +

      paste +

      +
    20. +
    + +
    + + diff --git a/plugin/mindmap/edit-mindmap/vendor/html/index.html b/plugin/mindmap/edit-mindmap/vendor/html/index.html new file mode 100644 index 000000000..1a14f26ce --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/html/index.html @@ -0,0 +1,105 @@ + + + + + kampfer's mindMap + + + + + + + + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    + + + + + + + + diff --git a/plugin/mindmap/edit-mindmap/vendor/html/index2.html b/plugin/mindmap/edit-mindmap/vendor/html/index2.html new file mode 100644 index 000000000..7f04b0935 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/html/index2.html @@ -0,0 +1,225 @@ + + + + + kampfer's mindMap + + + + + + +
    + +
    + + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    +
    As a thank you, we ask you to include an optional link back to Glyphicons whenever practical.
    + +
    +
    + + + + + + + + + + + +
    + + Holy guacamole! Best check yo self, you're not looking too good. +
    + + \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/html/template.html b/plugin/mindmap/edit-mindmap/vendor/html/template.html new file mode 100644 index 000000000..4751a914d --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/html/template.html @@ -0,0 +1,9 @@ + + + + + templates + + + diff --git a/plugin/mindmap/edit-mindmap/vendor/img/bg.gif b/plugin/mindmap/edit-mindmap/vendor/img/bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..c92e802e8c26797615f21b2fae73d32ec21a66a2 GIT binary patch literal 881 zcmZ?wbhEHblwgox_|Cxa{QbT6-=Dw#{{H_c7!84u7XpeuSr|Y-2SkAK1Ota3!+%a0 zj{t>+CJtdSn-v=#I<*V%y0x4rSahgMOfcz;M}e|?r@VaI8;eOx4l`&3uX5o`SbDlc f&i>dF3*%(pZgcf#Es;eJ{JLx%znzg3V6X-NBt0O_ literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings-white.png b/plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings.png b/plugin/mindmap/edit-mindmap/vendor/img/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/append_child_node.png b/plugin/mindmap/edit-mindmap/vendor/img/help/append_child_node.png new file mode 100644 index 0000000000000000000000000000000000000000..7d243359ca8a69e67490030343e9f8a29cfe28c3 GIT binary patch literal 9293 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/copy_node.png b/plugin/mindmap/edit-mindmap/vendor/img/help/copy_node.png new file mode 100644 index 0000000000000000000000000000000000000000..f7370c04117751efd8fe27834e79e5675f9de4c9 GIT binary patch literal 10231 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/create_new_map.png b/plugin/mindmap/edit-mindmap/vendor/img/help/create_new_map.png new file mode 100644 index 0000000000000000000000000000000000000000..c81cce27e2ee777b9c161f755cf38168fbe7428f GIT binary patch literal 10850 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 L>lfwEEQ0?JNi9DU literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/create_root_node.png b/plugin/mindmap/edit-mindmap/vendor/img/help/create_root_node.png new file mode 100644 index 0000000000000000000000000000000000000000..671003a38b343052e1a0122568e107ad723ee205 GIT binary patch literal 7330 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/delete.png b/plugin/mindmap/edit-mindmap/vendor/img/help/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..16ae1fde3cb243ffcf9df6a345da90e641f5e0b1 GIT binary patch literal 9518 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 a+!~u1R_%A4+rSwokeZ6NGD6WJ{67HaBed55 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/edit_node.png b/plugin/mindmap/edit-mindmap/vendor/img/help/edit_node.png new file mode 100644 index 0000000000000000000000000000000000000000..0ac5485b5aaaed894a1e7691af68438842bffdfe GIT binary patch literal 9272 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 IV;uN@0As!_&Hw-a literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/help.png b/plugin/mindmap/edit-mindmap/vendor/img/help/help.png new file mode 100644 index 0000000000000000000000000000000000000000..104b7e913828b63d7896ddbc90d1401b85be1d82 GIT binary patch literal 2780 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_disk.png b/plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..927f3b2a0f5765c9839005a2a1288215216a2133 GIT binary patch literal 11361 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 Pf&uC(n$IepT15OWjSmX7 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_storage.png b/plugin/mindmap/edit-mindmap/vendor/img/help/open_map_in_storage.png new file mode 100644 index 0000000000000000000000000000000000000000..b40433de43be7365c7aa92c14532ceab28168638 GIT binary patch literal 11206 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 X#Sz|B30#D`w*XC5kV@qf>#+X??vc3V literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/paste_node.png b/plugin/mindmap/edit-mindmap/vendor/img/help/paste_node.png new file mode 100644 index 0000000000000000000000000000000000000000..5a61c06714c48e3f8be12769fdae813abdeb7b6f GIT binary patch literal 9462 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/redo1.png b/plugin/mindmap/edit-mindmap/vendor/img/help/redo1.png new file mode 100644 index 0000000000000000000000000000000000000000..e535ea1f8278b02a916366448e94479e3c187553 GIT binary patch literal 16596 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 QPXr((1vU9{IrHHE1Mvxs_5c6? literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/redo2.png b/plugin/mindmap/edit-mindmap/vendor/img/help/redo2.png new file mode 100644 index 0000000000000000000000000000000000000000..0c26a1f99e1fed1e72c813f78ed8531293a51214 GIT binary patch literal 10514 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 QkC8}QLr=Y24IcG>0Ef$9p#T5? literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/save_map_in_disk.png b/plugin/mindmap/edit-mindmap/vendor/img/help/save_map_in_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..2f3f6f83964a6afa9c86a645babf29b86c8d67f0 GIT binary patch literal 12104 zcmZX)bwHF+^Dj;}f=H);N(ro_G}7S0(ny1plqd^Ghe|3Xwd9h^0@5X264J2b5{oF^ z-Rxa_@B97T-@SiqJm)#*%$zxMW%YH<;vP~wW2O%zvU(`!#fl?`pT6LED)dD&uKCS$)`6{i zwT#!U)Y#jq4vye&+eo#ib7@1sW`5H{gQ`bOGIifynxwofDoD01@^n@&y)yrOIKXQd z|CIJu@#{=I&*5^c+^dY&H+`4i+CVpIhT?=z_n%ibQ$ngO#H#kDqL0}FHhm1;X-=T0 zS0fK=Lg3GkZe&Kp`Ena!S=5FYA(-qE!;^pT{BTLRLD@fY#yR~5!T4DYy8A@A_D`lcv zpg@Tp{HF0t>2%>H1Vg|0Jehb=R%zUOyWVs|B)f3J9 z3TqawM=-uYoxfMnq32fJ5p-+GvWKHDqZ>>ek%RT>^BA{Effe;D8fQ;AUf`s4%~y@e z^W!N~x5xK?!x6FQP}O{_jV70Uve;yFATE>i0Rhu>A(|mCvyT8H8xJ$=3L=bLBr$S< z#I`r%Wnm2r3~UdHqy9czdC25B9(LxafBAEZ8P>M#O_#kHkCQJ?_HU&w-@t{HhNpR5 z`elinUwghBIE-Z3mA4It;K%t;G3^@}IzNH#*k*~aEPjC}f@GwBv7=i~T=tF7oGMcB z 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 DW?EWlllUuD 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/undo1.png b/plugin/mindmap/edit-mindmap/vendor/img/help/undo1.png new file mode 100644 index 0000000000000000000000000000000000000000..8800f8345a6e8b4c87c4f93c4a34775de24abb92 GIT binary patch literal 17048 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/img/help/undo2.png b/plugin/mindmap/edit-mindmap/vendor/img/help/undo2.png new file mode 100644 index 0000000000000000000000000000000000000000..349c53f44e5f73ad866daa21aa3a9b03af21c6ed GIT binary patch literal 10502 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 literal 0 HcmV?d00001 diff --git a/plugin/mindmap/edit-mindmap/vendor/index.html b/plugin/mindmap/edit-mindmap/vendor/index.html new file mode 100644 index 000000000..6973fe199 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/index.html @@ -0,0 +1,112 @@ + + + + + Kampfer's MindMap + + + + + + + +

    + + + + + + + + +
    + +
    + + + + + + + diff --git a/plugin/mindmap/edit-mindmap/vendor/js/MapManager.js b/plugin/mindmap/edit-mindmap/vendor/js/MapManager.js new file mode 100644 index 000000000..9c0974430 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/js/MapManager.js @@ -0,0 +1,255 @@ +/*global kampfer*/ +kampfer.require('Class'); + +kampfer.provide('mindMap.MapManager'); + +/* + * MapManager类为mindmap提供数据支持。 + * 注意:1.MapManager依赖的第三方组件store.js依赖JSON,当浏览器不原生支持JSON时, + * 需要额外引入JSON.js实现兼容。 + * 2.MapManager提供的查询方法返回的都是对数据的引用,因此它们都是只读的,绝对不要直接 + * 对它们进行写操作。 + */ + +kampfer.mindMap.MapManager = kampfer.Class.extend({ + + /** + * 实例化一个MapManager对象。 + * 当传递给构建函数的参数是一个对象,那么这个对象被用作mindmap数据来源 + * @param name{string|object|null} + */ + initializer : function(data) { + this._oriData = data; + + data = data || {}; + this._nodeTree = data.document || []; + this._mapName = data.name; + this._lastModified = data.lastModified || +new Date(); + //map用于快速查找 + this._nodeMap = this.parseTree(this._nodeTree); + this._isModified = false; + }, + + _isModified : null, + + //重命名map的时候先改变_mapName.保存时会检查_mapName和_mapData.name的一致性, + //两者如果不一致那么会删除localstore中名字叫_mapData.name的数据.并将_mapName + //和_mapData.name同步. 这个过程需要同时保存新名字和旧名字,_mapName保存新名字 + //_mapData.name是旧名字。 + _mapName : null, + + //解析原始数据, 通过tree生成map + parseTree : function(data) { + var map = {}; + + for(var i = 0, root; (root = data[i]); i++) { + this.traverseNode(root, function(node) { + map[node.id] = node; + }); + } + + return map; + }, + + isModified : function() { + return this._isModified; + }, + + getMapName : function() { + return this._mapName; + }, + + getNode : function(id) { + return this._nodeMap[id]; + }, + + getChildren : function(id) { + if(id) { + var node = this.getNode(id); + return node.children; + } + }, + + getNodeTree : function() { + return this._nodeTree; + }, + + //如果有同id的node那么新的node不会被添加 + addNode : function(node) { + if( kampfer.type(node) === 'object' && !this.getNode(node.id) ) { + var parent = this.getNode(node.parent); + + //add node to nodeTree + if(parent) { + if(!parent.children) { + parent.children = []; + } + parent.children.push(node); + } else { + this._nodeTree.push(node); + } + + //保存node和它的子节点的引用 + this.traverseNode(node, function(node) { + if( !(node.id in this._nodeMap) ) { + this._nodeMap[node.id] = node; + } + }); + + this._isModified = true; + } + }, + + createNode : function(data) { + var node = { + id : this.generateUniqueId(), + parent : null, + children : null, + content : 'node', + offset : { + x : 100, + y : 100 + } + }, + type = kampfer.type(data); + + if( type === 'object' ) { + for(var attr in node) { + if(attr in data) { + if(attr === 'offset') { + node[attr].x = data[attr].x; + node[attr].y = data[attr].y; + } else { + node[attr] = data[attr]; + } + } + } + } else if( type === 'string' ) { + node.parent = data; + } + + return node; + }, + + //此方法会立即改变children数组的长度 + deleteNode : function(id) { + var node = this.getNode(id), parent, nodeList; + + if(!node) { + return; + } + + parent = this.getNode(node.parent); + if(!parent) { + nodeList = this._nodeTree; + } else if(parent.children) { + nodeList = parent.children; + } + if(nodeList) { + //delete node from tree + for(var i = 0, c; (c = nodeList[i]); i++) { + if(c.id === id) { + nodeList.splice(i, 1); + break; + } + } + } + //delete node from map + this.traverseNode(node, function(node) { + delete this._nodeMap[node.id]; + }); + + this._isModified = true; + + return node; + }, + + setNodeContent : function(id, text) { + var node = this.getNode(id); + node.content = text; + this._isModified = true; + }, + + getNodeContent : function(id) { + var node = this.getNode(id); + return node.content; + }, + + setNodePosition : function(id, x, y) { + if(kampfer.type(id) === 'object') { + id = id.id; + } + var node = this.getNode(id); + node.offset.x = x; + node.offset.y = y; + this._isModified = true; + }, + + //传值 + getNodePosition : function(id) { + var node = this.getNode(id); + return { + left : node.offset.x, + top : node.offset.y + }; + }, + + setMapName : function(name) { + this._mapName = name; + this._isModified = true; + }, + + getMapData : function() { + return { + document : this._nodeTree, + name : this._mapName, + lastModified : this._lastModified + }; + }, + + traverseNode : function(node, callback, forward) { + if(forward) { + callback.call(this, node); + } + if(node.children) { + for(var i = 0, child; (child = node.children[i]); i++) { + this.traverseNode(child, callback, forward); + } + } + if(!forward) { + callback.call(this, node); + } + }, + + traverse : function(callback) { + for(var i = 0, node; (node = this._nodeTree[i]); i++) { + this.traverseNode(node, callback, true); + } + }, + + /* + * 生成唯一id + * 直接使用时间戳不可行 + * 以下方法摘自http://www.cnblogs.com/NoRoad/archive/2010/03/12/1684759.html + */ + generateUniqueId : function() { + var guid = ""; + for(var i = 1; i <= 32; i++) { + var n = Math.floor(Math.random() * 16.0).toString(16); + guid += n; + if((i == 8) || (i == 12) || (i == 16) || (i == 20)) { + guid += "-"; + } + } + return guid; + }, + + dispose : function() { + delete this._oriData; + delete this._nodeTree; + delete this._mapName; + delete this._lastModified; + delete this._nodeMap; + delete this._isModified; + } +}); \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/js/MapsManager.js b/plugin/mindmap/edit-mindmap/vendor/js/MapsManager.js new file mode 100644 index 000000000..515085765 --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/js/MapsManager.js @@ -0,0 +1,125 @@ +/*global kampfer*/ +kampfer.require('Class'); +kampfer.require('store'); + +kampfer.provide('mindMap.MapsManager'); + +/* + * 负责维护localStorage + * MapsManager提供的查询方法返回的都是对数据的引用,因此它们都是只读的。 + * 绝对不要直接对它们进行写操作。 + * localStorage目前维护两部分内容: + * 1. map的信息 + * 2. clipboard的信息 + */ + +kampfer.mindMap.MapsManager = kampfer.Class.extend({ + + initializer : function(appName) { + if(appName) { + this._appName = appName; + } + }, + + _appName : 'mindMap', + + getAppName : function() { + return this._appName; + }, + + //从localStorage中读取mindMap保存的数据。 + //如果没有任何数据,那么就创建一个新的空的数据对象,并将它写入 localStorage。 + getMapStorage : function() { + var mapStore = kampfer.store.get(this._appName); + if(!mapStore) { + mapStore = {}; + + mapStore.maps = {}; + mapStore.maps._count = 0; + + kampfer.store.set(this._appName, mapStore); + } + return mapStore; + }, + + getMapData : function(name) { + var mapStore = this.getMapStorage(); + if(name) { + return mapStore.maps[name]; + } + }, + + //只接受object作为参数 + saveMapToLocalStorage : function(data) { + if( kampfer.type(data) === 'object' ) { + var mapStorage = this.getMapStorage(), + name = data.name; + if( !(name in mapStorage.maps) ) { + mapStorage.maps._count++; + } + mapStorage.maps[name] = data; + kampfer.store.set(this._appName, mapStorage); + } + }, + + getMapCount : function() { + var mapStorage = this.getMapStorage(); + return mapStorage.maps._count; + }, + + //返回包含所有map名字的数组 + getMapList : function() { + var mapStorage = this.getMapStorage(); + if(mapStorage.maps._count > 0) { + var ret = []; + for(var map in mapStorage.maps) { + if( map !== '_count') { + ret.push(mapStorage.maps[map]); + } + } + return ret; + } + }, + + hasMap : function(mapName) { + var mapStore = kampfer.store.get(this._appName); + if(mapStore) { + if(mapName in mapStore.maps) { + return true; + } + } + return false; + }, + + removeMap : function(mapName) { + if( this.hasMap(mapName) ) { + var mapStore = kampfer.store.get(this._appName); + delete mapStore.maps[mapName]; + kampfer.store.set(this._appName, mapStore); + } + }, + + setClipboard : function(data) { + var mapStore = kampfer.store.get(this._appName); + if(mapStore) { + mapStore.clipboard = data; + kampfer.store.set(this._appName, mapStore); + } + }, + + getClipboard : function() { + var mapStore = kampfer.store.get(this._appName); + if(mapStore && mapStore.clipboard) { + return mapStore.clipboard; + } + }, + + removeClipboard : function() { + var mapStore = kampfer.store.get(this._appName); + if(mapStore && mapStore.clipboard) { + delete mapStore.clipboard; + kampfer.store.set(this._appName, mapStore); + } + } + +}); \ No newline at end of file diff --git a/plugin/mindmap/edit-mindmap/vendor/js/base.js b/plugin/mindmap/edit-mindmap/vendor/js/base.js new file mode 100644 index 000000000..48aa6bf8a --- /dev/null +++ b/plugin/mindmap/edit-mindmap/vendor/js/base.js @@ -0,0 +1,369 @@ +/* + * @Author : l.w.kampfer@gmail.com + */ + +(function(global) { + + var COMPILED = false; + + var kampfer = {}; + + kampfer.global = global; + + kampfer.basePath = ''; + + kampfer.implicitNamespaces = {}; + + kampfer.isProvided = function(name) { + return !kampfer.implicitNamespaces[name] && !!kampfer.getPropertyByName(name); + }; + + kampfer.getPropertyByName = function( name, obj ) { + var namespace = name.split('.'), + cur = obj || kampfer; + for( var part; (part = namespace.shift()); ) { + if( kampfer.isDefAndNotNull( cur[part] ) ) { + cur = cur[part]; + } else { + return null; + } + } + return cur; + }; + + kampfer.exportPath = function(name, value, objectToExportTo) { + + var cur = objectToExportTo || kampfer.global, + namespace = name.split('.'); + + for( var part; (part = namespace.shift()); ) { + if( !namespace.length && kampfer.isDefAndNotNull(value) ) { + cur[part] = value; + } else if( cur[part] ) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } + + }; + + kampfer.provide = function(name) { + + //if(!COMPILED) { + + if( kampfer.isProvided(name) ) { + throw Error('Namespace "' + name + '" already declared.'); + } + + delete kampfer.implicitNamespaces[name]; + + var namespace = name; + while( (namespace = namespace.substring( 0, namespace.lastIndexOf('.') )) ) { + if( kampfer.getPropertyByName(namespace) ) { + break; + } else { + kampfer.implicitNamespaces[namespace] = true; + } + } + //} + + kampfer.exportPath(name, null, kampfer); + + }; + + kampfer.require = function(name) { + + if( !COMPILED ) { + if ( !name || kampfer.isProvided(name) ) { + return; + } + + var path = kampfer._getPathFromDeps(name); + if (path) { + kampfer._included[path] = true; + kampfer._writeScripts(); + } + } + + }; + + kampfer.addDependency = function( path, provides, requires ) { + + if( !COMPILED ) { + + var provide, require, deps; + path = path.replace(/\\/g, '/'); + deps = kampfer._dependencies; + + for( var i = 0; (provide = provides[i]); i++) { + deps.nameToPath[provide] = path; + if (!(path in deps.pathToNames)) { + deps.pathToNames[path] = {}; + } + deps.pathToNames[path][provide] = true; + } + + for( var j = 0; (require = requires[j]); j++) { + if (!(path in deps.requires)) { + deps.requires[path] = {}; + } + deps.requires[path][require] = true; + } + + } + + }; + + + if(!COMPILED) { + + kampfer._inHtmlDocument = function() { + var doc = kampfer.global.document; + return typeof doc != 'undefined' && 'write' in doc; + // XULDocument misses write. + }; + + kampfer._getPathFromDeps = function(name) { + if( name in kampfer._dependencies.nameToPath ) { + return kampfer._dependencies.nameToPath[name]; + } else { + return null; + } + }; + + kampfer._importScript = function(src) { + var _importScript = kampfer._writeScriptTag; + if(!kampfer._dependencies.written[src] && _importScript(src)) { + kampfer._dependencies.written[src] = true; + } + }; + + kampfer._writeScripts = function() { + + var scripts = [], + seenScript = {}, + deps = kampfer._dependencies; + + function visitNode(path) { + if( path in deps.written ) { + return; + } + + if( path in deps.visited ) { + if( !(path in seenScript) ) { + seenScript[path] = true; + scripts.push(path); + } + return; + } + + deps.visited[path] = true; + + if (path in deps.requires) { + for (var requireName in deps.requires[path]) { + + if (!kampfer.isProvided(requireName)) { + if (requireName in deps.nameToPath) { + visitNode(deps.nameToPath[requireName]); + } else { + throw Error('Undefined nameToPath for ' + requireName + ' in ' + path); + } + } + } + } + + if (!(path in seenScript)) { + seenScript[path] = true; + scripts.push(path); + } + } + + for( var path in kampfer._included ) { + if( !deps.written[path] ) { + visitNode(path); + } + } + + for( var i = 0; i < scripts.length; i++ ) { + if ( scripts[i] ) { + kampfer._importScript( kampfer.basePath + scripts[i] ); + } else { + throw Error('Undefined script input'); + } + } + }; + + + kampfer._writeScriptTag = function(src) { + if( kampfer._inHtmlDocument() ) { + var doc = kampfer.global.document; + doc.write(' +', + $this->getService( + $this->getActiveServiceId() + )->getCustomUserURL($userInfo['username'], $userInfo['id'], $this->salt()) + ); + } + + /** + * Generates the redirect user specific URL for redirection. + * + * @throws Exception on hash generation failure + * + * @return string the specific user redirect URL + */ + public function getActiveServiceSpecificUserUrl() + { + $userInfo = api_get_user_info(); + + return $this->getService( + $this->getActiveServiceId() + )->getCustomUserRedirectURL($userInfo['id'], $this->salt()); + } +} diff --git a/plugin/userremoteservice/uninstall.php b/plugin/userremoteservice/uninstall.php new file mode 100644 index 000000000..c6ec7c868 --- /dev/null +++ b/plugin/userremoteservice/uninstall.php @@ -0,0 +1,6 @@ +uninstall(); diff --git a/plugin/vchamilo/README.md b/plugin/vchamilo/README.md new file mode 100644 index 000000000..ca5f05dd8 --- /dev/null +++ b/plugin/vchamilo/README.md @@ -0,0 +1,142 @@ +Virtual Chamilo +=================== + +Authors : Valery Fremaux (valery.fremaux@gmail.com), Julio Montoya, Angel Quiroz, Yannick Warnier + +Virtual Chamilo (or vChamilo) is a feature that allows you to easily run several chamilo instances +sharing the same code base, with separate documents and databases, acting mostly like containers +sharing the same libraries. + +With vChamilo, your first Chamilo portal acts as a container (or "controller"), and a seed + (or image) for the Chamilo instances you will install afterwards. This first image should be + created automatically when configuring the plugin, but if it isn't, the plugin will tell you + so and ask to generate one through the interface. + +Once the plugin is fully setup, you will be able to create new Chamilo instances in a + matter of minutes (or seconds on powerful servers). + +Changelog +========= + +*Version 1.7* + +Added support for (optional) custom CSS folder +Database upgrade needed: + + ALTER TABLE vchamilo ADD COLUMN css_theme_folder varchar(255); + + +*Version 1.6* + +Database upgrade needed: + + ALTER TABLE vchamilo ADD COLUMN archive_url varchar(255); + ALTER TABLE vchamilo ADD COLUMN home_url varchar(255); + ALTER TABLE vchamilo ADD COLUMN upload_url varchar(255); + ALTER TABLE vchamilo ADD COLUMN course_url varchar(255); + +*Version 1.5* + +Improved usability and added validations. No DB update required. + +*Version 1.4* + +Database upgrade needed: + + ALTER TABLE vchamilo ADD COLUMN password_encryption VARCHAR(255); + +*Version 1.3* + +Added vchamilo import + + +Version features +=================== +This is still a beta version and it is not fully featured with back-office tools. +As such, you will be able to create, edit, copy, delete and even upgrade instances, but a certain +amount of manual work will still be required at the web server level to get your instances running. + +How to setup +=================== + +To set this plugin up, you will need to: + +1. Insert the virtualization hook into the Chamilo master configuration file and enable multi-urls: + +``` +/app/config/configuration.php +``` + +Insert the hook at the end of the file. + +``` +include_once $_configuration['root_sys'].'plugin/vchamilo/lib/Virtual.php'; +Virtual::hookConfiguration($_configuration); +``` +And add (or uncomment) the line to enable multi-url: + +``` +$_configuration['multiple_access_urls'] = true; +``` +At this point, make sure there is no caching mechanism maintaining the previous configuration + version before you continue. Enabling the multi-url option should have the immediate effect + of adding a multi-url management link at the bottom of the "Platform" block in the + administration main page. + +Take a moment to update the configuration of the default host in the multi-url configuration page +to the real hostname of your main (controller) portal. + +2. Change the permissions on the /plugin/vchamilo/templates/ directory as it will + be necessary for the plugin to create files and directories there +3. Create a common directory to be used for all Chamilo-related files. + We recommend using /var/ for that. Inside that directory, create the following 4 + directories: cache/, courses/, home/ and upload/ and give permissions to the web user to write + into them (exactly the same way you did it for the app/ directory when installing Chamilo) +4. Enable and configure the plugin in the Chamilo administration's plugins list + (if in doubt, use the suggested values). Please note that the proxy configuration part is + totally optional and is not maintained by the Chamilo team at this point. +5. Enable additional virtual hosts in your Apache config (unless you use subdirectories). All virtual hosts should point to the same DocumentRoot as the initial Chamilo installation. +6. For each virtual host or subdirectory, you will need to configure specific redirection rules (remember, this is still at beta-level): + +``` +RewriteEngine On +RewriteRule /app/upload/(.*)$ http://[vchamilo-instance-domain]/[selected-common-dir]/upload/[vchamilo-instance-dir]/$1 [QSA,L] +``` +In the example above, you would need to replace everything that is currently within brackets, with +the specific details of each instance. For example: +``` +RewriteRule /app/upload/(.*)$ http://beeznest.chamilo.net/var/upload/beeznest-chamilo-net/$1 [QSA,L] +``` +Although your vChamilo instances *will* work basically without this rewrite rule, you will end +up observing issues of files not appearing while uploading files on the instance. + +Note that the domain of the instance, in the last part of the path, will be transformed +from dot-separated domain (beeznest.chamilo.net) to dash-separated-domain (beeznest-chamilo-net). + +7. Finally, go to the "Instances manager" and create new instances. Once an instance has been +created, make sure you edit it and set the "Cache URL" (previously "Archive URL"), "Home URL" and "Upload URL". These should +have the form of the "Web root" of the instance + the common directory + {cache|home|upload} + the +instance domain with dots changed to hyphens. + +For example, if you have used "var/" as a common directory and your instance URL (Web Root) is +https://chamilo1.beeznest.com/, then your "Cache URL" would look like this: + + https://chamilo1.beeznest.com/var/cache/chamilo1-beeznest-com/ + +These settings might be complex to setup at first, but once you've created your first instance, +creating a hundred more should be easy. + +Important note about file system permissions +------------- + +vChamilo instances *need* a central directory where to store all their files. You should create +that directory (as mentioned in point 3 above) and make sure it has the right permissions. +The plugin/vchamilo/templates/ directory also needs to be writeable by the web server. + +Additional notes +------------- + +The vChamilo plugin can work in combination with the "Chamilo Shell":https://github.com/chamilo/chamilo-lms +(or "chash") to ensure a good level of automatization of heavy processes, like upgrading to a higher version. +Although upgrading still has a manual process (in part), it will certainly save you dozens of hours if +upgrading dozens of portals. diff --git a/plugin/vchamilo/ajax/service.php b/plugin/vchamilo/ajax/service.php new file mode 100644 index 000000000..1776ee045 --- /dev/null +++ b/plugin/vchamilo/ajax/service.php @@ -0,0 +1,25 @@ +Success
    '; + } else { + echo 'Failure
    '.$errors.'
    '; + } + break; +} + +exit; diff --git a/plugin/vchamilo/cli/bulkcreatenodes.php b/plugin/vchamilo/cli/bulkcreatenodes.php new file mode 100644 index 000000000..4d0677663 --- /dev/null +++ b/plugin/vchamilo/cli/bulkcreatenodes.php @@ -0,0 +1,218 @@ + false, + 'help' => false, + 'config' => false, + 'nodes' => '', + 'lint' => false, + 'verbose' => false + ), + array( + 'h' => 'help', + 'i' => 'interactive', + 'c' => 'config', + 'n' => 'nodes', + 'l' => 'lint', + 'v' => 'verbose' + ) +); + +$interactive = !empty($options['interactive']); + +if ($unrecognized) { + $unrecognized = implode("\n ", $unrecognized); + cli_error("Cli unkown options\n".$unrecognized); +} + +if ($options['help']) { + $help = "Command line VChamilo Generator. +Please note you must execute this script with the same uid as apache! + +Options: +--interactive Blocks on each step and waits for input to continue +-h, --help Print out this help +-c, --config Define an external config file +-n, --nodes A node descriptor CSV file +-l, --lint Decodes node file and give a report on nodes to be created. + +Example: +\$sudo -u www-data /usr/bin/php /var/www/chamilo/plugin/vchamilo/cli/bulkcreatenodes.php --nodes= +"; //TODO: localize - to be translated later when everything is finished + + echo $help; + die; +} + +// Get all options from config file. + +if (!empty($options['config'])) { + echo "Loading config : ".$options['config']; + if (!file_exists($options['config'])) { + cli_error('Config file mentioned but not found'); + } + + $content = file($options['config']); + foreach ($content as $l) { + if (preg_match('/^\s+$/', $l)) continue; // Empty lines. + if (preg_match('/^[#\/!;]/', $l)) continue; // Comments (any form). + if (preg_match('/^(.*?)=(.*)$/', $l, $matches)) { + if (in_array($matches[1], $expectedoptions)) { + $options[trim($matches[1])] = trim($matches[2]); + } + } + } +} + +require_once($_configuration['root_sys'].'local/classes/database.class.php'); // cli only functions +require_once($_configuration['root_sys'].'local/classes/textlib.class.php'); // cli only functions +require_once($_configuration['root_sys'].'local/classes/mootochamlib.php'); // moodle like API +require_once($_configuration['root_sys'].'/plugin/vchamilo/lib/vchamilo_plugin.class.php'); + +global $DB; +if ($options['verbose']) echo "building database manager\n"; +$DB = new DatabaseManager(); +if ($options['verbose']) echo "building plugin vchamilo\n"; +$plugin = VChamiloPlugin::create(); + +if (empty($options['nodes'])) { + cli_error('Missing node definition. Halt.'); +} + +if ($options['verbose']) echo "parsing nodelist\n"; +$nodes = vchamilo_parse_csv_nodelist($options['nodes'], $plugin); + +if ($options['lint']) { + ctrace("Lint mode:\n"); + print_object($nodes); + die; +} + +if (empty($nodes)) { + cli_error('Node list empty'); +} + +ctrace('Starting generation'); + +// Get main admin for further replacement. +$admin = $DB->get_record('user', array('username' => 'admin')); + +foreach ($nodes as $data) { + + ctrace('Making node '.$data->root_web); + + if (!empty($data->template)) { + ctrace('Using template '.$data->template); + + if (!vchamilo_template_exists($data->template)) { + ctrace('Template not found. Skipping node.'); + continue; + } + } + + if ($DB->get_record('vchamilo', array('root_web' => $data->root_web))) { + ctrace('Node exists. skipping'); + continue; + } + + $data->what = 'addinstance'; + $data->registeronly = false; + + $NDB = null; + + $automation = true; + $return = include($_configuration['root_sys'].'plugin/vchamilo/views/editinstance.controller.php'); + if ($return == -1) { + cli_error('Node create process error'); + } + + // This is specific code for presetting any plugin data per instance from the CSV + ctrace('Setting up ent_installer'); + if ($NDB) { + // Copy admin account info from master + $NDB->set_field('user', 'password', $admin->password, array('username' => 'admin'), 'user_id'); + + // Setting ENT_installer values + if (!empty($data->ent_installer)) { + foreach ($data->ent_installer as $setting => $value) { + $settingrec = new StdClass(); + $settingrec->variable = 'ent_installer_'.$setting; + $settingrec->subkey = 'ent_installer'; + $settingrec->type = 'setting'; + $settingrec->category = 'Plugins'; + $settingrec->access_url = 1; + $settingrec->selected_value = $value; + ctrace("Setting up {$settingrec->variable}|{$settingrec->subkey} to $value\n"); + if ($oldrec = $NDB->get_record('settings_current', array('variable' => $settingrec->variable, 'subkey' => $settingrec->subkey, 'type' => $settingrec->type))) { + $settingrec->id = $oldrec->id; + $NDB->update_record('settings_current', $settingrec, 'id'); + } else { + $NDB->insert_record('settings_current', $settingrec); + } + } + } + + // updating other config values + if (!empty($data->config)) { + ctrace("VChamilo has config overrides\n"); + foreach ($data->config as $configkey => $configsetting) { + ctrace("Setting up {$configkey}"); + // Note you can just alter existing settings here as we cannot pull enough data from csv headers to get a complete setting descriptor. + $settingrec = new StdClass(); + $settingrec->variable = $configkey; + if (!empty($settingrec->subkey)) { + $settingrec->subkey = $configsetting->subkey; + } + // $settingrec->type = 'setting'; + // $settingrec->category = 'Plugins'; + // $settingrec->access_url = 1; + $settingrec->selected_value = $configsetting->value; + + if (!empty($settingrec->subkey)) { + $params = array('variable' => $settingrec->variable, 'subkey' => $settingrec->subkey); + } else { + $params = array('variable' => $settingrec->variable); + } + + if ($oldrec = $NDB->get_record('settings_current', $params)) { + ctrace("Updating {$settingrec->variable}|{$settingrec->subkey} to $configsetting->value\n"); + $settingrec->id = $oldrec->id; + $NDB->update_record('settings_current', $settingrec, 'id'); + } + } + } + $NDB->dismount(); + } else { + ctrace('No Side CNX for setup'); + } + + if ($interactive) { + $input = readline("Continue (y/n|r) ?\n"); + if ($input == 'r' || $input == 'R') { + // do nothing, just continue + } elseif ($input == 'n' || $input == 'N') { + echo "finishing\n"; + exit; + } + } +} diff --git a/plugin/vchamilo/cli/bulkdestroynodes.php b/plugin/vchamilo/cli/bulkdestroynodes.php new file mode 100644 index 000000000..01af0ac6f --- /dev/null +++ b/plugin/vchamilo/cli/bulkdestroynodes.php @@ -0,0 +1,170 @@ + false, + 'help' => false, + 'config' => false, + 'nodes' => '', + 'lint' => false + ), + array( + 'h' => 'help', + 'c' => 'config', + 'n' => 'nodes', + 'i' => 'interactive', + 'l' => 'lint' + ) +); + +$interactive = !empty($options['interactive']); + +if ($unrecognized) { + $unrecognized = implode("\n ", $unrecognized); + cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); +} + +if ($options['help']) { + $help = +"Command line VMoodle Generator. +Please note you must execute this script with the same uid as apache! + +Options: +--interactive No interactive questions or confirmations +-h, --help Print out this help +-c, --config Define an external config file +-n, --nodes A node descriptor CSV file +-l, --lint Decodes node file and give a report on nodes to be created. + +Example: +\$sudo -u www-data /usr/bin/php /var/www/chamilo19/plugin/vchamilo/cli/bulkdestroynodes.php --nodes= +"; //TODO: localize - to be translated later when everything is finished + + echo $help; + die; +} + +// Get all options from config file. +if (!empty($options['config'])) { + echo "Loading config : ".$options['config']; + if (!file_exists($options['config'])) { + cli_error(get_string('confignotfound', 'block_vmoodle')); + } + $content = file($options['config']); + foreach ($content as $l) { + if (preg_match('/^\s+$/', $l)) { + continue; // Empty lines. + } + if (preg_match('/^[#\/!;]/', $l)) { + continue; // Comments (any form). + } + if (preg_match('/^(.*?)=(.*)$/', $l, $matches)) { + if (in_array($matches[1], $expectedoptions)) { + $options[trim($matches[1])] = trim($matches[2]); + } + } + } +} + +require_once($_configuration['root_sys'].'local/classes/database.class.php'); // cli only functions +if ($options['verbose']) { + echo "loaded dbclass\n"; +} +require_once($_configuration['root_sys'].'local/classes/textlib.class.php'); // cli only functions +if ($options['verbose']) { + echo "loaded textlib\n"; +} +require_once($_configuration['root_sys'].'local/classes/mootochamlib.php'); // moodle like API +if ($options['verbose']) { + echo "loaded moodle wrapping\n"; +} +require_once($_configuration['root_sys'].'/plugin/vchamilo/lib/vchamilo_plugin.class.php'); +if ($options['verbose']) { + echo "loaded vchamilo plugin\n"; +} + +global $DB; +if ($options['verbose']) { + echo "building database manager\n"; +} +$DB = new DatabaseManager(); +if ($options['verbose']) { + echo "building plugin vchamilo\n"; +} +$plugin = VChamiloPlugin::create(); + +if (empty($options['nodes'])) { + cli_error(get_string('climissingnodes', 'block_vmoodle')); +} + +if ($options['verbose']) { + echo "parsing nodelist\n"; +} +$nodes = vchamilo_parse_csv_nodelist($options['nodes'], $plugin); + +if ($options['lint']) { + ctrace("Lint mode:\n"); + print_object($nodes); + die; +} + +if (empty($nodes)) { + cli_error(get_string('cliemptynodelist', 'block_vmoodle')); +} + +ctrace('Starting CLI processing'); + +foreach ($nodes as $n) { + + ctrace('Destroying node :'.$n->vhostname); + + if (!$DB->get_record('vchamilo', array('root_web' => $n->root_web))) { + ctrace('Node does not exist. Skipping'); + continue; + } + + /* + * This launches automatically all steps of the controller.management.php script several times + * with the "doadd" action and progressing in steps. + */ + $action = "fulldeleteinstances"; + + $automation = true; + + $return = include($_configuration['root_sys'].'/plugin/vchamilo/views/manage.controller.php'); + if ($interactive) { + $input = readline("Continue (y/n|r) ?\n"); + if ($input == 'r' || $input == 'R') { + $vmoodlestep--; + } elseif ($input == 'n' || $input == 'N') { + echo "finishing\n"; + exit(0); + } + } +} +exit (0); diff --git a/plugin/vchamilo/cli/clilib.php b/plugin/vchamilo/cli/clilib.php new file mode 100644 index 000000000..63cc22dfb --- /dev/null +++ b/plugin/vchamilo/cli/clilib.php @@ -0,0 +1,343 @@ + 1, + 'sitename' => 1, + 'institution' => 1, + 'main_database' => 1, + 'statistics_database' => 1, + 'user_personal_database' => 1, + 'db_user' => 1, + 'db_password' => 1, + 'course_folder' => 1, + ); + + $optional = array( + 'db_host' => 1, + 'template' => 1, + 'table_prefix' => 1, + 'single_database' => 1, + 'tracking_enabled' => 1, + 'visible' => 1, + ); + + $optionalDefaults = array( + 'db_host' => $_configuration['db_host'], + 'db_prefix' => 'chm_', + 'table_prefix' => '', + 'tracking_enabled' => 0, + 'single_database' => 1, + 'template' => '', + 'visible' => 1 + ); + + $patterns = array(); + + // Metas are accepted patterns (optional) + $metas = array( + 'plugin_.*', + 'config_.*' + ); + + // Get header (field names) + + $textlib = new textlib(); + + if (!$fp = fopen($nodelistlocation, 'rb')) { + cli_error($plugin->get_lang('badnodefile', 'vchamilo', $nodelistlocation)); + } + + // Jump any empty or comment line + $text = fgets($fp, 1024); + $i = 0; + while (vchamilo_is_empty_line_or_format($text, $i == 0)) { + $text = fgets($fp, 1024); + $i++; + } + + $headers = explode($csv_delimiter2, $text); + + // Check for valid field names + foreach ($headers as $h) { + $header[] = trim($h); + $patternized = implode('|', $patterns)."\\d+"; + $metapattern = implode('|', $metas); + if (!(isset($required[$h]) || + isset($optionalDefaults[$h]) || + isset($optional[$h]) || + preg_match("/$patternized/", $h) || + preg_match("/$metapattern/", $h))) { + cli_error("Node parse : invalidfieldname $h "); + return; + } + + if (isset($required[trim($h)])) { + $required[trim($h)] = 0; + } + } + + $expectedcols = count($headers); + $i++; + + // Check for required fields. + foreach ($required as $key => $value) { + if ($value) { // Required field missing. + cli_error("fieldrequired $key"); + return; + } + } + $linenum = 2; // Since header is line 1. + + // Take some from admin profile, other fixed by hardcoded defaults. + while (!feof($fp)) { + + // Make a new base record. + $vnode = new StdClass(); + foreach ($optionalDefaults as $key => $value) { + $vnode->$key = $value; + } + + //Note: commas within a field should be encoded as , (for comma separated csv files) + //Note: semicolon within a field should be encoded as ; (for semicolon separated csv files) + $text = fgets($fp, 1024); + if (vchamilo_is_empty_line_or_format($text, false)) { + $i++; + continue; + } + + $valueset = explode($csv_delimiter2, $text); + if (count($valueset) != $expectedcols) { + cli_error('wrong line count at line '.$i); + } + $f = 0; + foreach ($valueset as $value) { + // Decode encoded commas. + $key = $headers[$f]; + if (preg_match('/\|/', $key)) { + list($plugin, $variable) = explode('|', str_replace('plugin_', '', $key)); + if (empty($variable)) die("Key error in CSV : $key "); + if (!isset($vnode->$plugin)) { + $vnode->$plugin = new StdClass(); + } + $vnode->$plugin->$variable = trim($value); + } else { + if (preg_match('/^config_/', $key)) { + $smartkey = str_replace('config_', '', $key); + $keyparts = implode('|', $smartkey); + $keyvar = $keyparts[0]; + $subkey = @$keyparts[1]; + $vnode->config->$smartkey = new StdClass; + $vnode->config->$smartkey->subkey = $subkey; + $vnode->config->$smartkey->value = trim($value); + } else { + $vnode->$key = trim($value); + } + } + $f++; + } + $vnodes[] = $vnode; + } + + return $vnodes; +} + +/** + * Check a CSV input line format for empty or commented lines + * Ensures compatbility to UTF-8 BOM or unBOM formats + * @param resource $text + * @param bool $resetfirst + * @return bool + */ +function vchamilo_is_empty_line_or_format(&$text, $resetfirst = false) { + global $CFG; + + static $textlib; + static $first = true; + + // We may have a risk the BOM is present on first line + if ($resetfirst) $first = true; + if (!isset($textlib)) $textlib = new textlib(); // Singleton + $text = $textlib->trim_utf8_bom($text); + $first = false; + + $text = preg_replace("/\n?\r?/", '', $text); + + // last chance + if ('ASCII' == mb_detect_encoding($text)) { + $text = utf8_encode($text); + } + + // Check the text is empty or comment line and answer true if it is. + return preg_match('/^$/', $text) || preg_match('/^(\(|\[|-|#|\/| )/', $text); +} + +/** + * Get input from user + * @param string $prompt text prompt, should include possible options + * @param string $default default value when enter pressed + * @param array $options list of allowed options, empty means any text + * @param bool $casesensitiveoptions true if options are case sensitive + * @return string entered text + */ +function cli_input($prompt, $default = '', array $options = null, $casesensitiveoptions = false) { + echo $prompt; + echo "\n: "; + $input = fread(STDIN, 2048); + $input = trim($input); + if ($input === '') { + $input = $default; + } + if ($options) { + if (!$casesensitiveoptions) { + $input = strtolower($input); + } + if (!in_array($input, $options)) { + echo "Incorrect value, please retry.\n"; // TODO: localize, mark as needed in install + return cli_input($prompt, $default, $options, $casesensitiveoptions); + } + } + return $input; +} + +/** + * Returns cli script parameters. + * @param array $longoptions array of --style options ex:('verbose'=>false) + * @param array $shortmapping array describing mapping of short to long style options ex:('h'=>'help', 'v'=>'verbose') + * @return array array of arrays, options, unrecognised as optionlongname=>value + */ +function cli_get_params(array $longoptions, array $shortmapping = null) { + $shortmapping = (array) $shortmapping; + $options = array(); + $unrecognized = array(); + + if (empty($_SERVER['argv'])) { + // Bad luck, we can continue in interactive mode ;-) + return array($options, $unrecognized); + } + $rawoptions = $_SERVER['argv']; + + // Remove anything after '--', options can not be there. + if (($key = array_search('--', $rawoptions)) !== false) { + $rawoptions = array_slice($rawoptions, 0, $key); + } + + // Remove script. + unset($rawoptions[0]); + foreach ($rawoptions as $raw) { + if (substr($raw, 0, 2) === '--') { + $value = substr($raw, 2); + $parts = explode('=', $value); + if (count($parts) == 1) { + $key = reset($parts); + $value = true; + } else { + $key = array_shift($parts); + $value = implode('=', $parts); + } + if (array_key_exists($key, $longoptions)) { + $options[$key] = $value; + } else { + $unrecognized[] = $raw; + } + + } else if (substr($raw, 0, 1) === '-') { + $value = substr($raw, 1); + $parts = explode('=', $value); + if (count($parts) == 1) { + $key = reset($parts); + $value = true; + } else { + $key = array_shift($parts); + $value = implode('=', $parts); + } + if (array_key_exists($key, $shortmapping)) { + $options[$shortmapping[$key]] = $value; + } else { + $unrecognized[] = $raw; + } + } else { + $unrecognized[] = $raw; + continue; + } + } + // Apply defaults. + foreach ($longoptions as $key=>$default) { + if (!array_key_exists($key, $options)) { + $options[$key] = $default; + } + } + // Finished. + return array($options, $unrecognized); +} + +/** + * Print or return section separator string + * @param bool $return false means print, true return as string + * @return mixed void or string + */ +function cli_separator($return = false) { + $separator = str_repeat('-', 79)."\n"; + if ($return) { + return $separator; + } else { + echo $separator; + } +} + +/** + * Print or return section heading string + * @param string $string text + * @param bool $return false means print, true return as string + * @return mixed void or string + */ +function cli_heading($string, $return = false) { + $string = "== $string ==\n"; + if ($return) { + return $string; + } else { + echo $string; + } +} + +/** + * Write error notification + * @param $text + * @return void + */ +function cli_problem($text) { + fwrite(STDERR, $text."\n"); +} + +/** + * Write to standard out and error with exit in error. + * + * @param string $text + * @param int $errorCode + * @return void (does not return) + */ +function cli_error($text, $errorCode = 1) { + fwrite(STDERR, $text); + fwrite(STDERR, "\n"); + die($errorCode); +} diff --git a/plugin/vchamilo/cli/cron.php b/plugin/vchamilo/cli/cron.php new file mode 100644 index 000000000..612679739 --- /dev/null +++ b/plugin/vchamilo/cli/cron.php @@ -0,0 +1,77 @@ + false, + 'host' => false, + ), + array( + 'h' => 'help', + 'H' => 'host' + ) +); + +if ($unrecognized) { + $unrecognized = implode("\n ", $unrecognized); + cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); +} + +if ($options['help']) { + $help = +"Command line chamilo CRON + +Options: +-h, --help Print out this help +-H, --host Set the host (physical or virtual) to operate on + +"; //TODO: localize - to be translated later when everything is finished + + echo $help; + die; +} + +if (!empty($options['host'])) { + // arms the vchamilo switching + echo('Arming for '.$options['host']."\n"); // mtrace not yet available. + define('CLI_VCHAMILO_OVERRIDE', $options['host']); +} +// replay full config whenever. If vchamilo switch is armed, will switch now config +require $_configuration['root_sys'].'app/config/configuration.php'; // do REALLY force configuration to play again, or the following call will not have config tweaked (require_once) +echo('Config check : playing for '.$_configuration['root_web']."\n"); + +error_log('[chamilo][cronjob] Starting cron jobs as process '.getmypid()); +echo '
    ';
    +echo ('[chamilo][cronjob] Starting cron jobs as process '.getmypid()."\n");
    +require_once $_configuration['root_sys'].'main/cron/notification.php';
    +error_log('[chamilo][cronjob] Ending cron jobs of process '.getmypid());
    +echo('[chamilo][cronjob] Ending cron jobs of process '.getmypid()."\n");
    +echo '
    '; diff --git a/plugin/vchamilo/cli/maincron.php b/plugin/vchamilo/cli/maincron.php new file mode 100644 index 000000000..027e0942a --- /dev/null +++ b/plugin/vchamilo/cli/maincron.php @@ -0,0 +1,25 @@ +get_lang('hostlist'); + +$table = Database::get_main_table('vchamilo'); +$sql = "SELECT sitename, root_web FROM $table WHERE visible = 1"; + +if ($virtualChamilo == '%') { + $result = Database::query($sql); + $_template['hosts'] = []; + if ($result) { + while ($vchamilo = Database::fetch_assoc($result)) { + $_template['hosts'][] = $vchamilo; + } + } +} diff --git a/plugin/vchamilo/install.php b/plugin/vchamilo/install.php new file mode 100644 index 000000000..6093add95 --- /dev/null +++ b/plugin/vchamilo/install.php @@ -0,0 +1,79 @@ +'); + + $.get(url, function (data) { + $('#row_'+settingid).html(spare); + $('#res_'+settingid).html(data); + } ); +} \ No newline at end of file diff --git a/plugin/vchamilo/js/host_form.js b/plugin/vchamilo/js/host_form.js new file mode 100644 index 000000000..d7bad4795 --- /dev/null +++ b/plugin/vchamilo/js/host_form.js @@ -0,0 +1,62 @@ +/** + * Pop-up testing connection with database. + */ +function opencnxpopup(webroot) { + // Inputted data. + var dbhost = document.getElementById('id_vdbhost').value; + var dblogin = document.getElementById('id_vdbuser').value; + var dbpass = document.getElementById('id_vdbpassword').value; + + // PHP file linked the pop-up, and name. + var url = webroot+"/plugin/vchamilo/views/manage.testcnx.php" + "?" + "vdbhost=" + dbhost + "&" + "vdblogin=" + dblogin + + "&" + "vdbpass=" + dbpass; + // Pop-up's options. + var options = "width=500,height=300,toolbar=no,menubar=no,location=no,scrollbars=no,status=no"; + + // Opening the pop-up (title not working in Firefox). + var windowobj = window.open(url, '', options); +} + +/** + * Activates/desactivates services selection. + */ +function switcherServices(mnetnewsubnetwork) { + + // Retrieve 'select' elements from form. + var mnetenabled = document.getElementById('id_mnetenabled'); + var multimnet = document.getElementById('id_multimnet'); + var services = document.getElementById('id_services'); + + // Default values for services. + var mnetfreedefault = '0'; + var defaultservices = 'default'; + var subnetworkservices = 'subnetwork'; + + // Do the actions. + if (multimnet.value == mnetfreedefault + || multimnet.value == mnetnewsubnetwork) { + services.value = defaultservices; + services.disabled = true; + } else { + services.disabled = false; + services.value = subnetworkservices; + } +} + +function syncSchema(){ + + var originelement = document.getElementById("id_shortname"); + + var syncedelement2 = document.getElementById("id_vdbname"); + var syncedelement3 = document.getElementById("id_vdatapath"); + var syncedelement4 = document.getElementById("id_vhostname"); + + syncedelement2.value = syncedelement2.value.replace(/<%%INSTANCE%%>/g, originelement.value); + syncedelement3.value = syncedelement3.value.replace(/<%%INSTANCE%%>/g, originelement.value); + syncedelement4.value = syncedelement4.value.replace(/<%%INSTANCE%%>/g, originelement.value); +} + +function onLoadInit(){ + var originelement = document.getElementById("id_shortname"); + originelement.onchange = syncSchema; +} diff --git a/plugin/vchamilo/js/host_list.js b/plugin/vchamilo/js/host_list.js new file mode 100644 index 000000000..1708883e9 --- /dev/null +++ b/plugin/vchamilo/js/host_list.js @@ -0,0 +1,15 @@ +function selectallhosts() { + $('.vnodessel').attr('checked', true); +} + +function deselectallhosts() { + $('.vnodessel').attr('checked', false); +} + +function setpreset(form, select) { + presetvalue = select.options[select.selectedindex].value; + parts = presetvalue.split('/'); + + form.elements['variable'].value = parts[0]; + form.elements['subkey'].value = parts[1]; +} \ No newline at end of file diff --git a/plugin/vchamilo/lang/english.php b/plugin/vchamilo/lang/english.php new file mode 100644 index 000000000..bd79846da --- /dev/null +++ b/plugin/vchamilo/lang/english.php @@ -0,0 +1,123 @@ + + */ +$strings['archiverealroot'] = 'Cache container real root (system path, no symlinks here)'; +$strings['backtoindex'] = 'Back to instance index'; +$strings['badconnection'] = 'Connection FAILED'; +$strings['cancel'] = 'Cancel'; +$strings['choose'] = 'choose...'; +$strings['clearcache'] = 'Clear Twig cache'; +$strings['clearmastercache'] = 'Clear Twig cache for master'; +$strings['closewindow'] = 'Close this window'; +$strings['connectionok'] = 'Connection OK'; +$strings['continue'] = 'Continue'; +$strings['coursefolder'] = 'Physical dir'; +$strings['courserealroot'] = 'Course container real root (no symlinks here)'; +$strings['datalocation'] = 'Data location'; +$strings['datapathavailable'] = 'Data path is available and ready to be used as :
    '; +$strings['datapathnotavailable'] = 'Data path exists but has already files in it at
    '; +$strings['datapathcreated'] = 'Data path has been created as :
    '; +$strings['dbgroup'] = 'Database Settings'; +$strings['dbhost'] = 'Database Host'; +$strings['dbpassword'] = 'Password'; +$strings['dbprefix'] = 'Database prefix'; +$strings['dbuser'] = 'Username'; +$strings['deleteifempty'] = 'Delete if empty'; +$strings['deleteinstances'] = 'Remove instance'; +$strings['destroyinstances'] = 'Full delete instance'; +$strings['distributevalue'] = 'Distribute configuration value'; +$strings['edit'] = 'Edit instance info'; +$strings['emptysite'] = 'New empty site'; +$strings['enable_virtualisation'] = 'Enable'; +$strings['enabled'] = 'enabled'; +$strings['enableinstances'] = 'Enable'; +$strings['enabling'] = 'General enabling'; +$strings['errormuststartwithcourses'] = 'the course folder MUST start with \'courses_\' to avoid directory conflicts'; +$strings['homerealroot'] = 'Home container real root (no symlinks here)'; +$strings['hostdefinition'] = 'Host definition'; +$strings['hostlist'] = 'Other hosts'; +$strings['institution'] = 'Institution'; +$strings['lastcron'] = 'Last cron'; +$strings['maindatabase'] = 'Database name'; +$strings['manage_instances'] = 'Go to instance manager'; +$strings['newinstance'] = 'Add new instance'; +$strings['no'] = 'No'; +$strings['plugin_comment'] = 'Allows the virtualization of Chamilo. Use your main Chamilo installation as a hub, then create instances based on the same code but with different databases, and generate snapshots, and manage all instances like VM images.'; +$strings['plugin_title'] = 'Virtual Chamilo'; +$strings['proxysettings'] = 'Proxy settings'; +$strings['registerinstance'] = 'Register an instance'; +$strings['rootweb'] = 'Web root'; +$strings['savechanges'] = 'Save changes'; +$strings['selectall'] = 'Select all'; +$strings['selectnone'] = 'Deselect all'; +$strings['sendconfigvalue'] = 'Distribute a configuration value'; +$strings['setconfigvalue'] = 'Set a configuration value'; +$strings['singledatabase'] = 'Single database'; +$strings['sitename'] = 'Site Name'; +$strings['snapshotinstance'] = 'Snapshot'; +$strings['snapshotmaster'] = 'Snapshot master Chamilo'; +$strings['statisticsdatabase'] = 'Statistics database'; +$strings['successfinishedcapture'] = 'Snapshot of chamilo is finished'; +$strings['sync_settings'] = 'Synchronize settings'; +$strings['tableprefix'] = 'Table prefix'; +$strings['template'] = 'Template'; +$strings['templating'] = 'Templating'; +$strings['testconnection'] = 'Test database connexion'; +$strings['testdatapath'] = 'Test data location'; +$strings['userpersonaldatabase'] = 'User personal database'; +$strings['vchamilo'] = 'Virtual Chamilo'; +$strings['vchamilosnapshot1'] = 'STEP 1 OF 3 : Directories for snapshot have been created. Continue with database backup ...'; +$strings['vchamilosnapshot2'] = 'STEP 2 OF 3 : Databases have been backed up. Continue with saving files... beware this step can be long if a lot of content resides in the instance...'; +$strings['vchamilosnapshot3'] = 'STEP 3 OF 3 : Files saved.'; +$strings['withselection'] = 'With selection: '; +$strings['yes'] = 'Yes'; + +$strings['mysqlcmds'] = 'Mysql commands location'; +$strings['mysqlcmd'] = 'Full path to mysql client command'; +$strings['mysqldumpcms'] = 'Full path to mysqldump command'; + +$strings['sitenameinputerror'] = "Site Name is empty or invalid"; +$strings['institutioninputerror'] = "Institution is empty or invalid"; +$strings['rootwebinputerror'] = "Root web is empty or invalid"; +$strings['databaseinputerror'] = "Database empty"; +$strings['coursefolderinputerror'] = "Data location is empty"; + +$strings['httpproxyhost'] = "HTTP proxy host"; +$strings['httpproxyport'] = "HTTP proxy port"; +$strings['httpproxybypass'] = "HTTP proxy URL bypass"; +$strings['httpproxyuser'] = "HTTP proxy user"; +$strings['httpproxypassword'] = "HTTP proxy password"; + +$strings['variable'] = 'Variable'; +$strings['subkey'] = 'Subkey'; +$strings['category'] = 'Category'; +$strings['accessurl'] = 'Access URL'; +$strings['value'] = 'Value'; +$strings['syncall'] = 'Sync all the selection'; +$strings['syncthis'] = 'Sync this setting'; + +$strings['SiteNameExample'] = 'Example: Chamilo'; +$strings['InstitutionExample'] = 'Example: Chamilo Association'; +$strings['RootWebExample'] = 'Example: http://www.chamilo.org/ (with final slash)'; +$strings['DatabaseDescription'] = 'A new database will be created with that name.'; +$strings['RootWebExists'] = 'An instance with the same root web exists.'; +$strings['ImportInstance'] = 'Import instance'; +$strings['ConfigurationPath'] = 'Chamilo configuration path'; +$strings['UploadRealRoot'] = 'Upload files'; +$strings['DatabaseAccessShouldBeDifferentThanMasterChamilo'] = 'Database server should be different than the Chamilo master'; +$strings['UrlAppendExample'] = 'Example: /chamilo_v1 (with first slash)'; + +$strings['FromVersion'] = 'From version'; +$strings['CoursePath'] = 'Path to courses directory'; +$strings['HomePath'] = 'Path to home directory'; +$strings['UploadPath'] = 'Path to upload directory'; + +$strings['ArchiveUrl'] = 'Cache URL'; +$strings['HomeUrl'] = 'Home URL'; +$strings['UploadUrl'] = 'Upload URL'; +$strings['CourseUrl'] = 'Course URL'; +$strings['ThemeFolder'] = 'Theme folder'; +$strings['ThemeFolderExplanation'] = 'Theme folder should be located inside the web/css/themes/ folder'; diff --git a/plugin/vchamilo/lang/french.php b/plugin/vchamilo/lang/french.php new file mode 100644 index 000000000..9d9466a96 --- /dev/null +++ b/plugin/vchamilo/lang/french.php @@ -0,0 +1,108 @@ + + */ +$strings['archiverealroot'] = 'Container effectif de la cache (chemin réel sur disque)'; +$strings['backtoindex'] = 'Retour aux instances'; +$strings['cancel'] = 'Annuler'; +$strings['choose'] = 'Choisir une action...'; +$strings['closewindow'] = 'Fermer cette fenêtre'; +$strings['continue'] = 'Continuer'; +$strings['coursefolder'] = 'Répertoire des cours'; +$strings['courserealroot'] = 'Container effectif de cours (chemin réel)'; +$strings['clearcache'] = 'Vider le cache Twig'; +$strings['clearmastercache'] = 'Vider le cache Twig du maître'; +$strings['datalocation'] = 'Emplacement des données'; +$strings['datapathavailable'] = 'Le répertoire de données est disponible :
    '; +$strings['datapathnotavailable'] = 'Le répertoire de données existe mais a déjà des fichiers à l\'emplacement
    '; +$strings['datapathcreated'] = 'Le répertoire de données a été créé :
    '; +$strings['dbgroup'] = 'Réglages de base de données'; +$strings['dbhost'] = 'Hôte de BdD'; +$strings['dbpassword'] = 'Mot de passe'; +$strings['dbprefix'] = 'Préfix de base'; +$strings['dbuser'] = 'Utilisateur de BdD'; +$strings['deleteinstances'] = "Désactiver"; +$strings['destroyinstances'] = "Détruire"; +$strings['edit'] = 'Modifier les paramètres'; +$strings['emptysite'] = 'Nouveau site vide'; +$strings['enable_virtualisation'] = 'Activer'; +$strings['enabled'] = 'Etat'; +$strings['enableinstances'] = "Activer"; +$strings['enabling'] = 'Activation'; +$strings['errormuststartwithcourses'] = 'Le répertoire de cours doit commencer par \'courses_\' pour éviter les collisions de chemin'; +$strings['homerealroot'] = 'Container effectif de pages d\'accueil (chemin réel)'; +$strings['hostdefinition'] = 'Définition d\'hôte'; +$strings['hostlist'] = 'Les sites du réseau'; +$strings['institution'] = 'Institution'; +$strings['lastcron'] = 'Dernier cron'; +$strings['maindatabase'] = 'Nom de BdD'; +$strings['manage_instances'] = 'Aller au gestionnaire d\'instances'; +$strings['sync_settings'] = 'Synchroniser des réglages'; +$strings['deleteifempty'] = 'Supprimer la clef si vide'; +$strings['newinstance'] = "Ajouter une instance"; +$strings['no'] = 'Non'; +$strings['plugin_comment'] = 'Permettre les chamilos virtuels.'; +$strings['plugin_title'] = 'Virtualisation de Chamilo'; +$strings['proxysettings'] = 'Réglages du Proxy'; +$strings['registerinstance'] = "Enregistrer une instance"; +$strings['rootweb'] = 'Racine Web'; +$strings['savechanges'] = 'Enregistrer les modifications'; +$strings['selectall'] = 'Selectionner tout'; +$strings['selectnone'] = 'Déselectionner tout'; +$strings['setconfigvalue'] = 'Mettre à jour une variable de configuration'; +$strings['distributevalue'] = 'Distribuer la configuration'; +$strings['sendconfigvalue'] = 'Distribution d\'une valeur de configuration'; +$strings['singledatabase'] = 'Base de données unique'; +$strings['sitename'] = 'Nom du site'; +$strings['snapshotinstance'] = 'Snapshot'; +$strings['snapshotmaster'] = 'Snapshot du Chamilo maître'; +$strings['statisticsdatabase'] = 'base de données de statistiques'; +$strings['successfinishedcapture'] = 'La capture de Chamilo est terminée'; +$strings['tableprefix'] = 'Prefixe de table'; +$strings['template'] = 'Modèle'; +$strings['templating'] = 'Templating'; +$strings['testconnection'] = "Test de la connexion"; +$strings['testdatapath'] = "Test de l'emplacement de données"; +$strings['userpersonaldatabase'] = 'base de données des données personnelles'; +$strings['vchamilo'] = 'Chamilo Virtuel'; +$strings['vchamilosnapshot1'] = 'ETAPE 1 DE 3 : Les répertoires de snapshot ont été créés. Continuer avec la capture des bases...'; +$strings['vchamilosnapshot2'] = 'ETAPE 2 DE 3 : Les bases ont été capturées. Continuer avec la récupération des fichiers d\'usage... Attention, cette étape peut être longue si la plate-forme est fortement chargée en documents...'; +$strings['vchamilosnapshot3'] = 'ETAPE 3 DE 3 : Fichiers capturés.'; +$strings['withselection'] = "Avec la sélection : "; +$strings['yes'] = 'Oui'; + +$strings['mysqlcmds'] = 'Emplacement de la commande Mysql'; +$strings['mysqlcmd'] = 'Chemin vers le client mysql'; +$strings['mysqldumpcms'] = 'Chemin vers la commande mysqldump'; + +$strings['sitenameinputerror'] = "Le nom de site est vide"; +$strings['institutioninputerror'] = "L'institution est vide ou invalide"; +$strings['rootwebinputerror'] = "L'URL d'acces est vide ou invalide"; +$strings['databaseinputerror'] = "La base de données n'est pas définie"; +$strings['coursefolderinputerror'] = "Le répertoire du cours n'est pas défini"; + +$strings['httpproxyhost'] = "Hôte Proxy HTTP"; +$strings['httpproxyport'] = "Port Proxy HTTP"; +$strings['httpproxybypass'] = "Filtre d'URL de proxy (exclusions)"; +$strings['httpproxyuser'] = "Utilisateur Proxy"; +$strings['httpproxypassword'] = "Mot de passe Proxy"; + +$strings['variable'] = 'Variable'; +$strings['subkey'] = 'Clef'; +$strings['category'] = 'Catégorie'; +$strings['accessurl'] = 'Sous-site'; +$strings['value'] = 'Valeur'; +$strings['syncall'] = 'Synchroniser la sélection'; +$strings['syncthis'] = 'Synchroniser ce réglage'; + +$strings['SiteNameExample'] = 'Exemple: Chamilo'; +$strings['InstitutionExample'] = 'Exemple: Chamilo Association'; +$strings['RootWebExample'] = 'Exemple: http://www.chamilo.org/'; +$strings['DatabaseDescription'] = 'Une nouvelle base de données sera créée avec ce nom.'; +$strings['RootWebExists'] = 'Une instance avec le même nom existe déjà.'; +$strings['FromVersion'] = 'Version d\'origine'; +$strings['CoursePath'] = 'Chemin vers répertoire de cours'; +$strings['HomePath'] = 'Chemin vers répertoire home'; +$strings['UploadPath'] = 'Chemin vers répertoire upload'; diff --git a/plugin/vchamilo/lang/spanish.php b/plugin/vchamilo/lang/spanish.php new file mode 100644 index 000000000..e8a9b4110 --- /dev/null +++ b/plugin/vchamilo/lang/spanish.php @@ -0,0 +1,5 @@ + + * @author Julio Montoya + */ +class VChamiloPlugin extends Plugin +{ + /** + * VChamiloPlugin constructor. + */ + public function __construct() + { + parent::__construct('1.4', 'Valery Fremaux, Julio Montoya'); + } + + /** + * @return VChamiloPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @return string + */ + public function get_name() + { + return 'vchamilo'; + } +} diff --git a/plugin/vchamilo/lib/Virtual.php b/plugin/vchamilo/lib/Virtual.php new file mode 100644 index 000000000..f343e6bbc --- /dev/null +++ b/plugin/vchamilo/lib/Virtual.php @@ -0,0 +1,1388 @@ +executeQuery($query); + + if ($result->rowCount()) { + $data = $result->fetch(); + $excludes = ['id', 'name']; + $query = "SELECT * FROM settings_current WHERE subkey = 'vchamilo'"; + $virtualSettings = $connection->executeQuery($query); + $virtualSettings = $virtualSettings->fetchAll(); + + $homePath = ''; + $coursePath = ''; + $archivePath = ''; + $uploadPath = ''; + $passwordEncryption = ''; + foreach ($virtualSettings as $setting) { + switch ($setting['variable']) { + case 'vchamilo_upload_real_root': + $uploadPath = $setting['selected_value']; + break; + case 'vchamilo_home_real_root': + $homePath = $setting['selected_value']; + break; + case 'vchamilo_course_real_root': + $coursePath = $setting['selected_value']; + break; + case 'vchamilo_archive_real_root': + $archivePath = $setting['selected_value']; + break; + case 'vchamilo_password_encryption': + $passwordEncryption = $setting['selected_value']; + break; + } + } + + if (empty($homePath) || empty($coursePath) || empty($archivePath) || empty($uploadPath)) { + echo 'Configure correctly the vchamilo plugin'; + exit; + } + + // Only load if is visible + if ($data && $data['visible'] === '1') { + foreach ($data as $key => $value) { + if (!in_array($key, $excludes)) { + // Avoid empty password_encryption + if ($key == 'password_encryption' && empty($value)) { + continue; + } + $_configuration[$key] = $value; + } + $_configuration['virtual'] = $data['root_web'].'/'; + } + + $data['SYS_ARCHIVE_PATH'] = self::addTrailingSlash($archivePath).$data['slug']; + $data['SYS_HOME_PATH'] = self::addTrailingSlash($homePath).$data['slug']; + $data['SYS_COURSE_PATH'] = self::addTrailingSlash($coursePath).$data['slug']; + $data['SYS_UPLOAD_PATH'] = self::addTrailingSlash($uploadPath).$data['slug']; + + $data['WEB_HOME_PATH'] = self::addTrailingSlash($data['home_url']); + $data['WEB_UPLOAD_PATH'] = self::addTrailingSlash($data['upload_url']); + $data['WEB_ARCHIVE_PATH'] = self::addTrailingSlash($data['archive_url']); + + if (!empty($passwordEncryption)) { + $_configuration['password_encryption'] = $passwordEncryption; + } + + // Instance cannot have multiple urls + $_configuration['multiple_access_urls'] = false; + $_configuration['virtual_css_theme_folder'] = ''; + if (isset($data['css_theme_folder']) && !empty($data['css_theme_folder'])) { + $_configuration['virtual_css_theme_folder'] = $data['css_theme_folder']; + } + $virtualChamilo = $data; + } else { + exit("This portal is disabled. Please contact your administrator"); + } + } // otherwise it means the plugin was not configured yet + } + + /** + * @param array $_configuration + */ + public static function getHostName(&$_configuration) + { + if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && + $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_configuration['force_https_forwarded_proto']) + ) { + $protocol = 'https'; + } else { + if (!empty($_SERVER['HTTPS'])) { + $protocol = 'https'; + } else { + $protocol = 'http'; + } + } + + if (defined('CLI_VCHAMILO_OVERRIDE')) { + $_configuration['vchamilo_web_root'] = CLI_VCHAMILO_OVERRIDE; + $_configuration['vchamilo_name'] = preg_replace('#https?://#', '', CLI_VCHAMILO_OVERRIDE); + // remove radical from override for name + + // fake the server signature + global $_SERVER; + + $_SERVER['SERVER_NAME'] = $_configuration['vchamilo_name']; + $_SERVER['HTTP_HOST'] = $_configuration['vchamilo_name']; + $_SERVER['QUERY_STRING'] = ''; + $_SERVER['REQUEST_URI'] = CLI_VCHAMILO_OVERRIDE; + + return; + } + + $contentPrefix = '/'; + if (isset($_SERVER['CONTEXT_PREFIX']) && !empty($_SERVER['CONTEXT_PREFIX'])) { + $contentPrefix = $_SERVER['CONTEXT_PREFIX']; + } else { + // Getting url_append from URL + if (isset($_SERVER['REQUEST_URI'])) { + $requestUri = $_SERVER['REQUEST_URI']; + if (strpos($requestUri, '/courses/') !== false) { + $result = substr($requestUri, 0, strpos($requestUri, '/courses/')); + if (!empty($result) && $result != '/') { + $contentPrefix = $result; + } + } + } + } + + $_configuration['vchamilo_web_root'] = "{$protocol}://".@$_SERVER['HTTP_HOST'].$contentPrefix; + + $_configuration['vchamilo_name'] = @$_SERVER['HTTP_HOST']; + if (empty($_configuration['vchamilo_name'])) { // try again with another source if has failed + $_configuration['vchamilo_name'] = "{$protocol}://".$_SERVER['SERVER_NAME']; + if ($_SERVER['SERVER_PORT'] != 80) { + $_configuration['vchamilo_name'] .= ':'.$_SERVER['SERVER_PORT']; + } + $_configuration['vchamilo_name'] = $_SERVER['SERVER_NAME']; + } + } + + /** + * @param string $path + * + * @return string + */ + public static function addTrailingSlash($path) + { + return substr($path, -1) == '/' ? $path : $path.'/'; + } + + /** + * provides a side connection to a vchamilo database. + * + * @param array $_configuration + * + * @return \Doctrine\DBAL\Driver\Connection + */ + public static function bootConnection(&$_configuration) + { + $dbParams = [ + 'driver' => 'pdo_mysql', + 'host' => $_configuration['db_host'], + 'user' => $_configuration['db_user'], + 'password' => $_configuration['db_password'], + 'dbname' => isset($_configuration['main_database']) ? $_configuration['main_database'] : '', + // Only relevant for pdo_sqlite, specifies the path to the SQLite database. + 'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '', + // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8, + 'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '', + ]; + + try { + $database = new \Database(); + $connection = $database->connect( + $dbParams, + $_configuration['root_sys'], + $_configuration['root_sys'], + true + ); + } catch (Exception $e) { + echo 'Side connection failure with '.$_configuration['db_host'].', '.$_configuration['db_user'].', ******** '; + exit(); + } + + return $connection; + } + + /** + * @param string $url + */ + public static function redirect($url) + { + if (preg_match('#https?://#', $url)) { + header('location: '.$url); + } else { + header('location: '.api_get_path(WEB_PATH).$url); + } + exit; + } + + /** + * @param string $course_folder + * + * @return string + */ + public static function getHtaccessFragment($course_folder) + { + $str = " + # Change this file to fit your configuration and save it as .htaccess in the courses folder # + # Chamilo mod rewrite + # Comment lines start with # and are not processed + + + RewriteEngine On + + # Rewrite base is the dir chamilo is installed in with trailing slash + RewriteBase /{$course_folder}/ + + # Do not rewrite on the main dir + # Change this path to the path of your main folder + RewriteCond %{REQUEST_URI} !^/main/ + + #replace nasty ampersands by 3 slashes, we change these back in download.php + RewriteRule ([^/]+)/document/(.*)&(.*)$ $1/document/$2///$3 [N] + + # Rewrite everything in the scorm folder of a course to the download script + RewriteRule ([^/]+)/scorm/(.*)$ /main/document/download_scorm.php?doc_url=/$2&cDir=$1 [QSA,L] + + # Rewrite everything in the document folder of a course to the download script + RewriteRule ([^/]+)/document/(.*)$ /main/document/download.php?doc_url=/$2&cDir=$1 [QSA,L] + + # Rewrite everything in the work folder + RewriteRule ([^/]+)/work/(.*)$ /main/work/download.php?file=work/$2&cDir=$1 [QSA,L] + + "; + + return $str; + } + + /** + * @return string + */ + public static function getDefaultCourseIndexFragment() + { + return ""; + } + + /** + * @param string $template + * + * @return bool + */ + public static function templateExists($template) + { + global $_configuration; + + // Find and checktemplate directory (files and SQL). + $separator = DIRECTORY_SEPARATOR; + $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates'; + $relative_datadir = $templatefoldername.$separator.$template.'_sql'; + $absolute_datadir = $_configuration['root_sys'].$relative_datadir; + + return is_dir($absolute_datadir); + } + + /** + * drop a vchamilo instance databases using the physical connection. + * + * @param stdClass $params + * return an array of errors or false if ok + */ + public static function dropDatabase($params) + { + $params = clone $params; + + if (empty($params->main_database)) { + Display::addFlash(Display::return_message('No database found')); + + return; + } + + $databaseToDelete = $params->main_database; + unset($params->main_database); + $connection = self::getConnectionFromInstance($params); + if ($connection) { + $databases = $connection->getSchemaManager()->listDatabases(); + + if (in_array($databaseToDelete, $databases)) { + $connection->getSchemaManager()->dropDatabase( + $databaseToDelete + ); + Display::addFlash( + Display::return_message( + 'Database deleted: '.$databaseToDelete + ) + ); + } else { + Display::addFlash( + Display::return_message( + 'Database does not exist: '.$databaseToDelete + ) + ); + } + } else { + Display::addFlash( + Display::return_message( + "Cannot connect DB: $databaseToDelete" + ) + ); + } + + return false; + } + + /** + * @param stdClass $params + * + * @return bool + */ + public static function createDatabase($params) + { + $params = clone $params; + $databaseName = $params->main_database; + unset($params->main_database); + + $connection = self::getConnectionFromInstance($params); + if ($connection) { + $databaseList = $connection->getSchemaManager()->listDatabases(); + + if (!in_array($databaseName, $databaseList)) { + $connection->getSchemaManager()->createDatabase($databaseName); + Display::addFlash( + Display::return_message("Creating DB ".$databaseName) + ); + } else { + Display::addFlash( + Display::return_message("DB already exists: ".$databaseName) + ); + } + + return true; + } + + return false; + } + + /** + * get a proper SQLdump command. + * + * @param object $vchamilodata the complete new host information + * + * @return string the shell command + */ + public static function getDatabaseDumpCmd($vchamilodata) + { + $pgm = self::getConfig('vchamilo', 'mysql_cmd'); + + if (!$pgm) { + $pgm = '/usr/bin/mysql'; + } + + $phppgm = str_replace("\\", '/', $pgm); + $phppgm = str_replace("\"", '', $phppgm); + $pgm = str_replace("/", DIRECTORY_SEPARATOR, $pgm); + + if (!is_executable($phppgm)) { + throw new Exception('databasecommanddoesnotmatchanexecutablefile'); + } + + // Retrieves the host configuration (more secure). + $vchamilodata = empty($vchamilodata) ? self::makeThis() : $vchamilodata; + if (strstr($vchamilodata->db_host, ':') !== false) { + list($vchamilodata->db_host, $vchamilodata->db_port) = explode( + ':', + $vchamilodata->db_host + ); + } + + // Password. + $databasePassword = ''; + if (!empty($vchamilodata->db_password)) { + $databasePassword = '-p'.escapeshellarg($vchamilodata->db_password).' '; + } + + // Making the command line (see 'vconfig.php' file for defining the right paths). + $sqlcmd = $pgm.' -h'.$vchamilodata->db_host.(isset($vchamilodata->db_port) ? ' -P'.$vchamilodata->db_port.' ' : ' '); + $sqlcmd .= '-u'.$vchamilodata->db_user.' '.$databasePassword; + $sqlcmd .= '%DATABASE% < '; + + return $sqlcmd; + } + + /** + * @param stdClass $vchamilo + * @param string $template + * + * @return bool + */ + public static function loadDbTemplate($vchamilo, $template) + { + global $_configuration; + + // Make template directory (files and SQL). + $separator = DIRECTORY_SEPARATOR; + $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates'; + $absolute_datadir = $_configuration['root_sys'].$templatefoldername.$separator.$template.$separator.'dump.sql'; + + if (!$sqlcmd = self::getDatabaseDumpCmd($vchamilo)) { + return false; + } + + $sqlcmd = str_replace('%DATABASE%', $vchamilo->main_database, $sqlcmd); + + // Make final commands to execute, depending on the database type. + $import = $sqlcmd.$absolute_datadir; + + // Execute the command. + Display::addFlash(Display::return_message("Load database from template dump: \n $import ")); + + if (!defined('CLI_SCRIPT')) { + putenv('LANG=en_US.utf-8'); + } + // ensure utf8 is correctly handled by php exec() + // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input + + exec($import, $output, $return); + if (!empty($output)) { + Display::addFlash(Display::return_message(implode("\n", $output)."\n")); + } + + return true; + } + + /** + * Backups a database for having a snapshot. + * + * @param $vchamilo object The Vchamilo object + * @param $outputfilerad string The output SQL file radical + * + * @return bool if TRUE, dumping database was a success, otherwise FALSE + */ + public static function backupDatabase($vchamilo, $outputfilerad) + { + // Separating host and port, if sticked. + if (strstr($vchamilo->db_host, ':') !== false) { + list($host, $port) = explode(':', $vchamilo->db_host); + } else { + $host = $vchamilo->db_host; + } + + // By default, empty password. + $pass = ''; + $pgm = null; + + if (empty($port)) { + $port = 3306; + } + + // Password. + if (!empty($vchamilo->db_password)) { + $pass = "-p".escapeshellarg($vchamilo->db_password); + } + + // Making the commands for each database. + $cmds = []; + // Windows environments are not supported for this plugin at this time + //if ($CFG->ostype == 'WINDOWS') { + // $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}"; + // $cmds[] = $cmd_main . ' > ' . $outputfilerad; + //} else { + $cmd_main = "-h{$host} -P{$port} -u{$vchamilo->db_user} {$pass} {$vchamilo->main_database}"; + $cmds[] = $cmd_main.' > '.escapeshellarg($outputfilerad); + //} + + $mysqldumpcmd = self::getConfig('vchamilo', 'cmd_mysqldump', true); + + $pgm = !empty($mysqldumpcmd) ? stripslashes($mysqldumpcmd) : false; + + if (!$pgm) { + $message = "Database dump command not available check here: "; + $url = api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=vchamilo'; + $message .= Display::url($url, $url); + Display::addFlash(Display::return_message($message)); + + return false; + } else { + $phppgm = str_replace("\\", '/', $pgm); + $phppgm = str_replace("\"", '', $phppgm); + $pgm = str_replace('/', DIRECTORY_SEPARATOR, $pgm); + + if (!is_executable($phppgm)) { + $message = "Database dump command $phppgm does not match any executable"; + Display::addFlash(Display::return_message($message)); + + return false; + } + + // executing all commands + foreach ($cmds as $cmd) { + // Final command. + $cmd = $pgm.' '.$cmd; + + // Executes the SQL command. + exec($cmd, $execoutput, $returnvalue); + } + } + + // End with success. + return 1; + } + + /** + * read manifest values in vchamilo template. + */ + public static function getVmanifest($version) + { + $templatewwwroot = ''; + // Define the $templatewwwroot content, found in manifest.php for this template + $file = api_get_path(SYS_PATH).'/plugin/vchamilo/templates/'.$version.'/manifest.php'; + if (file_exists($file)) { + include $file; + + $manifest = new stdClass(); + $manifest->templatewwwroot = $templatewwwroot; + // $manifest->templatevdbprefix = $templatevdbprefix; + // $manifest->coursefolder = $coursefolder; + + return $manifest; + } + + return false; + } + + /** + * make a fake vchamilo that represents the current host. + */ + public static function makeThis() + { + global $_configuration; + + $thisPortal = new stdClass(); + $thisPortal->root_web = $_configuration['root_web']; + $thisPortal->db_host = $_configuration['db_host']; + $thisPortal->db_user = $_configuration['db_user']; + $thisPortal->db_password = $_configuration['db_password']; + $thisPortal->main_database = $_configuration['main_database']; + + return $thisPortal; + } + + /** + * Get available templates for defining a new virtual host. + * + * @return array the available templates, or EMPTY array + */ + public static function getAvailableTemplates() + { + global $_configuration; + + $separator = DIRECTORY_SEPARATOR; + + $templatefoldername = 'plugin'.$separator.'vchamilo'.$separator.'templates'; + $tempDir = $_configuration['root_sys'].$templatefoldername; + + // Scans the templates. + if (!is_dir($tempDir)) { + $mode = api_get_permissions_for_new_directories(); + mkdir($tempDir, $mode, true); + } + + $finder = new \Symfony\Component\Finder\Finder(); + $dirs = $finder->in($tempDir)->depth('== 0'); + + // Retrieves template(s) name(s). Should be hostnames. + $templates = []; + /*if ($addEmptyTemplate) { + $templates = array('' => $plugin->get_lang('emptysite')); + }*/ + + $template = self::getConfig('vchamilo', 'default_template'); + + if ($dirs) { + /** @var Symfony\Component\Finder\SplFileInfo $dir */ + foreach ($dirs as $dir) { + if (is_dir($dir->getPathname())) { + // A template is considered when a dump.sql exists. + if (file_exists($dir->getPathname().'/dump.sql')) { + $templateName = $dir->getRelativePathname(); + if ($templateName == $template) { + $templateName .= ' (default)'; + } + $templates[$dir->getRelativePathname()] = $templateName; + } + } + } + } + + return $templates; + } + + /** + * this function set will map standard moodle API calls to chamilo + * internal primitives. This avoids too many changes to do in imported + * code. + */ + public static function getConfig($module, $key, $isplugin = true) + { + if ($isplugin) { + $key = $module.'_'.$key; + } + + $params = ['variable = ? AND subkey = ?' => [$key, $module]]; + $result = api_get_settings_params_simple($params); + if ($result) { + return $result['selected_value']; + } + + return false; + } + + /** + * @param stdClass $vchamilo + * @param string $template + */ + public static function loadFilesFromTemplate($vchamilo, $template) + { + global $_configuration; + + // Make template directory (files and SQL). + $separator = DIRECTORY_SEPARATOR; + $templateDir = $_configuration['root_sys'].'plugin'.$separator.'vchamilo'.$separator.'templates'.$separator.$template; + $vchamilo->virtual = true; + $coursePath = self::getConfig('vchamilo', 'course_real_root').$separator.$vchamilo->slug; + $homePath = self::getConfig('vchamilo', 'home_real_root').$separator.$vchamilo->slug; + $archivePath = self::getConfig('vchamilo', 'archive_real_root').$separator.$vchamilo->slug; + $uploadPath = self::getConfig('vchamilo', 'upload_real_root').$separator.$vchamilo->slug; + + // get the protocol free hostname + Display::addFlash( + Display::return_message("Copying {$templateDir}/data/courses => $coursePath") + ); + + copyDirTo( + self::chopLastSlash($templateDir.'/data/courses'), + self::chopLastSlash($coursePath), + false + ); + + Display::addFlash( + Display::return_message("Copying {$templateDir}/data/archive => $archivePath") + ); + + copyDirTo( + self::chopLastSlash($templateDir.'/data/archive'), + self::chopLastSlash($archivePath), + false + ); + + Display::addFlash( + Display::return_message("Copying {$templateDir}/data/home => $homePath") + ); + + copyDirTo( + self::chopLastSlash($templateDir.'/data/home'), + self::chopLastSlash($homePath), + false + ); + + // Upload + Display::addFlash( + Display::return_message("Copying {$templateDir}/data/upload => $uploadPath") + ); + + copyDirTo( + self::chopLastSlash($templateDir.'/data/upload/'), + self::chopLastSlash($uploadPath), + false + ); + } + + /** + * @param string $path + * + * @return mixed + */ + public static function chopLastSlash($path) + { + return preg_replace('/\/$/', '', $path); + } + + /** + * @param string $str + */ + public static function ctrace($str) + { + error_log($str); + Display::addFlash(Display::return_message($str, 'normal', false)); + } + + /** + * @param $file + * @param $component + * @param bool $return + * + * @return string + */ + public static function requireJs($file, $component, $return = false) + { + global $_configuration, $htmlHeadXtra; + + if (preg_match('/^local_/', $component)) { + $component = str_replace('local_', '', $component); + $path = 'local/'; + } else { + $path = 'plugin/'; + } + + // Secure the postslashing of the roots. + $root_web = $_configuration['root_web'].'/'; + $root_web = preg_replace('#//$#', '/', $root_web); + + $str = ''."\n"; + if ($return === 'head') { + $htmlHeadXtra[] = $str; + } + + if ($return) { + return $str; + } + echo $str; + } + + /** + * @param $file + * @param $component + * @param bool $return + * + * @return string + */ + public static function requireCss($file, $component, $return = false) + { + global $_configuration, $htmlHeadXtra; + + if (preg_match('/^local_/', $component)) { + $component = str_replace('local_', '', $component); + $path = 'local/'; + } else { + $path = 'plugin/'; + } + + // Secure the postslashing of the roots. + $root_web = $_configuration['root_web'].'/'; + $root_web = preg_replace('#//$#', '/', $root_web); + + $str = ''."\n"; + if ($return === 'head') { + $htmlHeadXtra[] = $str; + } + if ($return) { + return $str; + } + echo $str; + } + + /** + * @param string $url + * + * @return string + */ + public static function getSlugFromUrl($url) + { + $slugify = new Slugify(); + $urlInfo = parse_url($url); + if (isset($urlInfo['host'])) { + $path = $urlInfo['path'] != '/' ? '_'.$urlInfo['path'] : ''; + + return $slugify->slugify($urlInfo['host'].$path); + } + + return false; + } + + /** + * Check if all settings are complete. + */ + public static function checkSettings() + { + $enabled = self::getConfig('vchamilo', 'enable_virtualisation'); + + if (empty($enabled)) { + api_not_allowed(true, 'Plugin is not enabled'); + } + + global $virtualChamilo; + if (!isset($virtualChamilo)) { + api_not_allowed( + true, + 'You have to edit the configuration.php. Please check the readme file.' + ); + } + + $coursePath = self::getConfig('vchamilo', 'course_real_root'); + $homePath = self::getConfig('vchamilo', 'home_real_root'); + $archivePath = self::getConfig('vchamilo', 'archive_real_root'); + $uploadPath = self::getConfig('vchamilo', 'upload_real_root'); + $cmdSql = self::getConfig('vchamilo', 'cmd_mysql'); + $cmdMySql = self::getConfig('vchamilo', 'cmd_mysqldump'); + + if (empty($coursePath) || empty($homePath) || empty($uploadPath) || empty($archivePath) || empty($cmdSql) || empty($cmdMySql)) { + api_not_allowed(true, 'You have to complete all plugin settings.'); + } + + $separator = DIRECTORY_SEPARATOR; + $templatePath = api_get_path(SYS_PATH).'plugin'.$separator.'vchamilo'.$separator.'templates'; + + $paths = [ + $coursePath, + $homePath, + $archivePath, + $uploadPath, + $templatePath, + ]; + + foreach ($paths as $path) { + $path = trim($path); + if (is_dir($path)) { + if (!is_writable($path)) { + Display::addFlash( + Display::return_message("Directory must have writable permissions: '$path'", 'warning') + ); + } + } else { + Display::addFlash( + Display::return_message("Directory doesn't exist: '$path'", 'warning') + ); + } + } + } + + /** + * @param object $instance + * + * @return bool|\Doctrine\DBAL\Connection + */ + public static function getConnectionFromInstance($instance, $getManager = false) + { + $dbParams = [ + 'driver' => 'pdo_mysql', + 'host' => $instance->db_host, + 'user' => $instance->db_user, + 'password' => $instance->db_password, + //'dbname' => $instance->main_database, + // Only relevant for pdo_sqlite, specifies the path to the SQLite database. + //'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '', + // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8, + //'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '', + ]; + + if (!empty($instance->main_database)) { + $dbParams['dbname'] = $instance->main_database; + } + + try { + $database = new \Database(); + $manager = $database->connect( + $dbParams, + api_get_configuration_value('root_sys'), + api_get_configuration_value('root_sys'), + false, + true + ); + + if ($getManager) { + return $manager; + } + + return $manager->getConnection(); + } catch (Exception $e) { + error_log($e->getMessage()); + } + + return false; + } + + public static function addInstance(stdClass $data) + { + if (isset($data->what)) { + unset($data->what); + } + if (isset($data->submitbutton)) { + unset($data->submitbutton); + } + if (isset($data->id)) { + unset($data->id); + } + if (isset($data->vid)) { + unset($data->vid); + } + if (isset($data->testconnection)) { + unset($data->testconnection); + } + if (isset($data->testdatapath)) { + unset($data->testdatapath); + } + + $registeronly = $data->registeronly; + unset($data->registeronly); + $data->lastcron = 0; + $data->lastcrongap = 0; + $data->croncount = 0; + + if (isset($data->template) && !empty($data->template)) { + $template = $data->template; + } else { + $template = ''; + } + + $data->main_database = Database::clearDatabaseName($data->main_database); + + $mainDatabase = api_get_configuration_value('main_database'); + + if ($mainDatabase == $data->main_database) { + Display::addFlash( + Display::return_message('You cannot use the same database as the chamilo master', 'error') + ); + + return; + } + + $databaseName = $data->main_database; + $data->main_database = ''; + $connection = self::getConnectionFromInstance($data); + $data->main_database = $databaseName; + if (!$connection) { + Display::addFlash( + Display::return_message( + 'Cannot connect to database with params: '.print_r($data, 1), + 'error' + ) + ); + + return; + } + + $data->root_web = api_add_trailing_slash($data->root_web); + + $data->archive_url = api_add_trailing_slash($data->archive_url); + $data->home_url = api_add_trailing_slash($data->home_url); + $data->upload_url = api_add_trailing_slash($data->upload_url); + $data->course_url = api_add_trailing_slash($data->course_url); + + if (substr($data->root_web, 0, 4) != 'http') { + $data->root_web = api_get_protocol().'://'.$data->root_web; + } + + self::ctrace('Registering: '.$data->root_web); + $tablename = Database::get_main_table('vchamilo'); + $sql = "SELECT * FROM $tablename + WHERE root_web = '".Database::escape_string($data->root_web)."'"; + $result = Database::query($sql); + + if (Database::num_rows($result)) { + Database::update($tablename, $data, ['root_web = ?' => $data->root_web]); + $virtualInfo = Database::fetch_array($result); + $slug = $virtualInfo['slug']; + } else { + $slug = $data->slug = self::getSlugFromUrl($data->root_web); + if (empty($slug)) { + Display::addFlash( + Display::return_message('Cannot create slug from url: '.$data->root_web, 'error') + ); + + return; + } + Database::insert($tablename, (array) $data); + } + + if ($registeronly) { + // Stop it now. + self::ctrace('Registering only. out.'); + self::redirect(api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php'); + } + + // or we continue with physical creation + self::createDirsFromSlug($slug); + + if (!$template) { + // Create empty database for install + self::ctrace("Creating database"); + self::createDatabase($data); + } else { + // Deploy template database + self::ctrace("Creating databases from template '$template'"); + self::createDatabase($data); + self::ctrace("Loading data template '$template'"); + self::loadDbTemplate($data, $template); + self::ctrace("Coying files from template '$template'"); + self::loadFilesFromTemplate($data, $template); + } + + // pluging in site name institution + $settingstable = $data->main_database.'.settings_current'; + $accessurltable = $data->main_database.'.access_url'; + + $sitename = Database::escape_string($data->sitename); + $institution = Database::escape_string($data->institution); + + $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$sitename}' + WHERE variable = 'siteName' AND category = 'Platform' "; + + $sqls[] = "UPDATE {$settingstable} SET selected_value = '{$institution}' + WHERE variable = 'institution' AND category = 'Platform' "; + + $sqls[] = "UPDATE {$accessurltable} SET url = '{$data->root_web}' WHERE id = '1' "; + + foreach ($sqls as $sql) { + Database::query($sql); + } + + self::ctrace("Finished"); + } + + /** + * @throws \Doctrine\DBAL\Exception + * + * @return false|void + */ + public static function importInstance(stdClass $data, string $fromVersion) + { + if (isset($data->what)) { + unset($data->what); + } + if (isset($data->submitbutton)) { + unset($data->submitbutton); + } + if (isset($data->id)) { + unset($data->id); + } + if (isset($data->vid)) { + unset($data->vid); + } + if (isset($data->testconnection)) { + unset($data->testconnection); + } + if (isset($data->testdatapath)) { + unset($data->testdatapath); + } + + $fromCoursePath = $data->course_path; + $fromHomePath = $data->home_path; + $fromUploadPath = $data->upload_path; + + unset($data->course_path); + unset($data->home_path); + unset($data->upload_path); + + $newDatabase = clone $data; + $newDatabase->main_database = Database::clearDatabaseName($newDatabase->import_to_main_database); + $newDatabase->db_user = $newDatabase->import_to_db_user; + $newDatabase->db_password = $newDatabase->import_to_db_password; + $newDatabase->db_host = $newDatabase->import_to_db_host; + + unset($newDatabase->import_to_main_database); + unset($newDatabase->import_to_db_user); + unset($newDatabase->import_to_db_password); + unset($newDatabase->import_to_db_host); + + unset($data->import_to_main_database); + unset($data->import_to_db_user); + unset($data->import_to_db_password); + unset($data->import_to_db_host); + + $data->lastcron = 0; + $data->lastcrongap = 0; + $data->croncount = 0; + + $mainDatabase = api_get_configuration_value('main_database'); + + if ($mainDatabase == $data->main_database) { + Display::addFlash( + Display::return_message('You cannot use the same database as the chamilo master', 'error') + ); + + return false; + } + + self::ctrace('Registering: '.$data->root_web); + + $table = Database::get_main_table('vchamilo'); + $sql = "SELECT * FROM $table + WHERE root_web = '".Database::escape_string($data->root_web)."'"; + $result = Database::query($sql); + $id = null; + if (Database::num_rows($result)) { + Display::addFlash( + Display::return_message('Instance was already added: '.$data->root_web, 'error') + ); + + return false; + } else { + /** @var EntityManager $em */ + $em = self::getConnectionFromInstance($data, true); + if ($em) { + $connection = $em->getConnection(); + $statement = $connection->query('SELECT * FROM settings_current'); + $settings = $statement->fetchAll(); + $settings = array_column( + $settings, + 'selected_value', + 'variable' + ); + $institution = $settings['Institution']; + $siteName = $settings['siteName']; + $newDatabase->sitename = $siteName; + $newDatabase->institution = $institution; + $slug = $newDatabase->slug = $data->slug = self::getSlugFromUrl($data->root_web); + $id = Database::insert($table, (array) $newDatabase); + } + } + + if (!$id) { + // Show data detail to help debug + //var_dump($data); + throw new Exception('New/Imported instance was not registered - edit '.__FILE__.' on line '.__LINE__.'to var_dump'); + } + + if (empty($slug)) { + throw new Exception('Slug is empty'); + } + + self::createDirsFromSlug($slug); + $databaseCreated = self::createDatabase($newDatabase); + if (!$databaseCreated) { + Display::addFlash( + Display::return_message('Error while creating a DB', 'error') + ); + + return false; + } + + $coursePath = self::getConfig('vchamilo', 'course_real_root').'/'.$slug; + $homePath = self::getConfig('vchamilo', 'home_real_root').'/'.$slug; + $uploadPath = self::getConfig('vchamilo', 'upload_real_root').'/'.$slug; + + $dumpFile = api_get_path(SYS_ARCHIVE_PATH).uniqid($data->main_database.'_dump_', true).'.sql'; + self::ctrace('Create backup from "'.$data->main_database.'" here: '.$dumpFile.' '); + self::backupDatabase($data, $dumpFile); + + $sqlcmd = self::getDatabaseDumpCmd($newDatabase); + $sqlcmd = str_replace('%DATABASE%', $newDatabase->main_database, $sqlcmd); + $import = $sqlcmd.$dumpFile; + + // Execute the command. + if (!defined('CLI_SCRIPT')) { + putenv('LANG=en_US.utf-8'); + } + + // ensure utf8 is correctly handled by php exec() + // @see http://stackoverflow.com/questions/10028925/call-a-program-via-shell-exec-with-utf-8-text-input + $result = exec($import, $output, $return); + + self::ctrace('Restore backup here "'.$newDatabase->main_database.'" :
    '.$import.' '); + self::ctrace($result); + + $command = new \Chash\Command\Installation\UpgradeDatabaseCommand(); + // Creates the helper set + $helperSet = \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($em); + + $helpers = [ + 'configuration' => new Chash\Helpers\ConfigurationHelper(), + 'dialog' => new \Symfony\Component\Console\Helper\QuestionHelper(), + ]; + + foreach ($helpers as $name => $helper) { + $helperSet->set($helper, $name); + } + + $command->setHelperSet($helperSet); + + $tmpFile = tmpfile(); + $outputStream = new \Symfony\Component\Console\Output\BufferedOutput($tmpFile); + + $arguments = [ + 'from-version' => $fromVersion, // @todo change value + 'to-version' => '1.11.x', + 'host' => $newDatabase->db_host, + 'username' => $newDatabase->db_user, + 'password' => $newDatabase->db_password, + 'db_name' => $newDatabase->main_database, + 'root_sys' => api_get_configuration_value('root_sys'), + ]; + + $input = new ArrayInput($arguments); + $command->run($input, $outputStream); + + error_log($outputStream->fetch()); + + if (file_exists($dumpFile)) { + unlink($dumpFile); + } + + // Course + self::ctrace("Copy from '$fromCoursePath' to backup '$coursePath' "); + copyDirTo( + self::chopLastSlash($fromCoursePath), + self::chopLastSlash($coursePath), + false + ); + + // Home + self::ctrace("Copy from '$fromHomePath' to backup '$homePath' "); + copyDirTo( + self::chopLastSlash($fromHomePath), + self::chopLastSlash($homePath), + false + ); + + // Upload + self::ctrace("Copy from '$fromUploadPath' to backup '$uploadPath' "); + copyDirTo( + self::chopLastSlash($fromUploadPath), + self::chopLastSlash($uploadPath), + false + ); + + self::ctrace("Finished"); + } + + /** + * @param string $slug + * + * @return string + */ + public static function createDirsFromSlug($slug) + { + // We continue with physical creation + + // Create course directory for operations. + // this is very important here (DO NOT USE api_get_path() !!) because storage may be remotely located + $absAlternateCourse = self::getConfig('vchamilo', 'course_real_root'); + $courseDir = $absAlternateCourse.'/'.$slug; + $mode = api_get_permissions_for_new_directories(); + if (!is_dir($courseDir)) { + self::ctrace("Creating physical course dir in $courseDir"); + mkdir($courseDir, $mode, true); + // initiate default index + $indexFile = $courseDir.'/index.html'; + if ($indexFile) { + file_put_contents($indexFile, self::getDefaultCourseIndexFragment()); + } + + $htaccessFile = $courseDir.'/.htaccess'; + if ($htaccessFile) { + file_put_contents($htaccessFile, self::getHtaccessFragment($slug)); + } + } + + $absAlternateHome = self::getConfig('vchamilo', 'home_real_root'); + $absAlternateArchive = self::getConfig('vchamilo', 'archive_real_root'); + $absAlternateUpload = self::getConfig('vchamilo', 'upload_real_root'); + + // absalternatehome is a vchamilo config setting that tells where the + // real physical storage for home pages are. + $homeDir = $absAlternateHome.'/'.$slug; + $archiveDir = $absAlternateArchive.'/'.$slug; + $uploadDir = $absAlternateUpload.'/'.$slug; + + $dirs = [ + $homeDir, + $archiveDir, + $uploadDir, + ]; + + foreach ($dirs as $dir) { + self::ctrace("Making dir as $dir"); + + if (!is_dir($dir)) { + if (!mkdir($dir, $mode, true)) { + self::ctrace("Error creating dir $dir \n"); + } + } + } + } + + /** + * @param $id + * + * @return array|mixed + */ + public static function getInstance($id) + { + $vhost = new stdClass(); + if ($id) { + $id = (int) $id; + $sql = "SELECT * FROM vchamilo WHERE id = $id"; + $result = Database::query($sql); + $vhost = (object) Database::fetch_array($result, 'ASSOC'); + } + + return $vhost; + } + + /** + * @param stdClass $instance + * + * @return bool|string returns the original version of the app + */ + public static function canBeUpgraded($instance) + { + $connection = self::getConnectionFromInstance($instance); + if ($connection) { + $sql = 'SELECT * FROM settings_current WHERE variable = "chamilo_database_version"'; + $statement = $connection->query($sql); + $settings = $statement->fetchAll(); + $settings = array_column($settings, 'selected_value', 'variable'); + $version = $settings['chamilo_database_version']; + $versionParts = explode('.', $version); + $version = implode('.', [$versionParts[0], $versionParts[1], '0']); + + $currentVersion = api_get_setting('chamilo_database_version'); + $versionParts = explode('.', $currentVersion); + $currentVersion = implode( + '.', + [$versionParts[0], $versionParts[1], '0'] + ); + + if (version_compare($version, $currentVersion, '<')) { + return $version; + } + } + + return false; + } + + /** + * @return array + */ + public static function getEncryptList() + { + $encryptList = [ + 'bcrypt', + 'sha1', + 'md5', + 'none', + ]; + + return array_combine($encryptList, $encryptList); + } +} diff --git a/plugin/vchamilo/pix/ajax_waiter.gif b/plugin/vchamilo/pix/ajax_waiter.gif new file mode 100644 index 0000000000000000000000000000000000000000..3980ff00b1bc7d845ea080629a1702c5a6d1ca11 GIT binary patch literal 14190 zcmds;cU05qzOU0rehKLzg%BVKA&JT;B05+%5E23+A~KF*84wF7B27h#lM11SDoqSU zKv6+J1avT=2?~lG%ec`R_(OmV#TW!U36bGPfPyY4>skF)&;mrGnOJ`c~gy?A=b zT;>P6!ssv!4A#=ra`*P#XFopc>FYVud1h>U?8C1gu3foyrsK@mgRwX7-n6u~+`V`A z&FeRV7YBQ~dY(Ob_Tl4)Yd5aJ{_lSA{+GG0G+?E~%}?qy(-8>=pT+wH1!2HPhsl3o z^1lHy`xqq1+7wnEdgOTc)6SzSIHcDu+E?>Tek5hz(($^*#CF;kJahp(+v8bd+&m`6 ztlrq-Zf-BToLOD`ME?*X8AC`&Sr85t=J5U}iKp#_`(lzp_HL&cHov`-Z!h#y3 zU2BbbMIoxXvrceChdfs}An4gpDs0NcqfvzoW#zZ+aCeuFW1lT+5&rOUSo|XM>c&L# z$M(3fpUESe-4Unu+2hDeD2S-KvKoX2;sW`Um6UqkQj&;BvxHh4Kf7jfo#jl%RqV{UJ&VI0Dt~6`#5U%9~nIRiiJ0Y_P&bHhJ7so%`QrdDoP_T1N_0EtM{e^R6*n)r;A2!{7y{+fW z?qU>NA>cj?zCWD&hSox} zGf_`;?cnTQYK@5bEsMX)(SNkaTRi>RAnUM62Wcv`?k~-?oQ=CIRWq{N{gdkvzN(0CMF#|*Z79iuqeTe`9kUEGtF+LFl51`4& z$^gMneMf7OH2@Ak44?#%0dN86n0+*e{)2$LJ_i$5WH=nSV@cC6bzkR=Xh8^y^H7u54%}JavrZs-36GIB?R(2En`DdtAvXN4wSzS$bi#_$$&7F$amfNFqt4zTLq@d&HAGQ9V_-AKL5v!?^|pG{(wyv|&>LMxX;u!4Ao8eEOYq1*8Og3lZG*7LxeZ@+ z^6AaSZ&E&EP3^kCbwCFo@u|2$nz7NbAn{FGHi5VS41f>-!={a!fE@rfAaRiX6b}HQ zbsN@A%KzToDSiM;Q-FnU3!h}EFdxtY!1&V-|NS4)AC3A*xdS`9zdCv#e8Is%c{2a8 zdZnkEda!7(#SL6antd zR%!CJ$=`Y*_Qu0Y4`7%Q%k<1lI!%s8X-ebw9$=utJxK|LN6RoeHu_K}6e2~a4`@$m z5uSKVepz`b2T$z0rk7#*F0~!^L^Sp`l;ty7*E+45B{s*0dWelSvb@G08Ko#_{1ClJ za__~(ru#T@n$CH5AG)cerXc_x+`8f6-uHM93i`pi%U0E*ZzS_SaQFSRY5Q^~^`)#8 zgR6cF;lxUB?5O(E$@L6h6{w7A@$u{ES zheb;kfl3810z?8jfxH4%1x&FBxb*S%nbHtY&woE$Fc`oq?Js!s3-f2=8QT6iuY=^c zjFcO{ROmXX@P{$^n>dO#v!G&&B3rjd#us}~SRV^&l9p;Awc|!*Wa60Na*9SR7FvQF)-?nH=OSzR4tdE6cK_XKO}u7sN?S^h}ri%|?dz3pQq&XMqH z_lpf@;u6y+zmR>4Bzu3BE&ffE!ldB$5j*d^^SiGn-MDvO61O9#eeF1c;by?!>r1&q zj0@C}*1GM#xj>&M#~i9J!y|6KvV1&qe6EL7+}l0P-v+t0J^ojG*d=pp z&+M=!hmJbdKASJYIWS)JU0Z+74!f;uY$RA>KvX2&YcdYWk5tIgcL$O4b(nhT>5CtR zT}?e}1M))s-s%spw`WmLpZPLIr@=CWzQY{?C6x32UaB}z(}gzv$l`vH8`Q1BqJ<6< zhQd@d9zQQUNKx@V*NH#x8r@AfH~-Oa)zX6>1)u46v>Y4GWXp4L5NHnI2Ur5KbC~G> zgf-Rpfbs@}1t<~-1%NTY6_DB#gwFGv0fAF}P=J4cfG+?T0|EiG0AOb)=gBVVk8TLC z2kIgK+hMxHWJ}d?1~3ma`mX~C@Js&-eqJeVm3sf=ftZa=3SUOv4a*}?CwG~B?4r3G z1)f`eKX&B29)0EUZ#*{6htAz+jZWgyr4&p|bWDytJkFD*O)1u;s=S1WNoYf*2$GQP z^*QxXd^wMiVn|A?v}P1YZTFQnRfy7QX4yyUEp4nkg~^q=?{q>>o~XE@%7EpGkMtge z@3?rgv*mp2(8-Eo2>JbURyz~ba($58ijozzzO^Qo}at5 z78&=t?|FI{?3*0-=8I8hP99r7+gtYY$PW*r?xjW-Z^3F6LEVlUpL?^uXo?E!B62f* zo-!lE2g8{Q@*{thqZaWR?-rif^)-ahefTxWXcZhWLYbMmj$MmI=r)zt@CR>`8%pta zpNu?T=wT|4B6FdM>M@C`x`Mujnp3GvK~p`*qv4w*`%zejMe;I14+f6)RghmQ-)YJH zc~&W1XWD;yv|RN$^Pv!^Nw;m5fWrV{Q@xf2)dHXdzywkR9N}?zz$SnQ_zbWE z2m&-sG0S0d0FOYTC^QOq4!{VI1k3`2r+P38h6TVD)NVj4o`?r{0>FVa0o{OcAW=Z# z{~Lw?z5KtRht#a-$X{K4?kC6lkf3bfzUAY9PJ~nx?cwaKXghO1c!pgXdE+J0G*=&M zL?R?kKv+x7h0&Q&Lilb^j9OcmoTBie#V3dl9Hh>WkTbHf*k*DHM|&V4{vgE3m7)@h zj+awYMd(^1KTt-~987Lcrg>7Rh2@3=(<$c08&;mw{>vSWp0uWOC&~+FAa5A$PfjB> zUh3mDb$b*vy@U>MuoJby5RcIZZg*Aj&lUgUqC=l=BD`ANTl;DO%&9{>(O+mzn`OrQJ) zNSd1PfZqTv0Jtf+0XnDr_G#J!iUULfGy!T;iUTlxQXD`EU^?0A{mY6&e{}t;-z-vA zl;j6!u3fboRk(7LkES_=yVni(*)6DK6J62VOR+9H4)Na%qdtBs;sq{6^EkRx4mQMt zgo%xb%@e}+dQP)|?_-zkgQFx?~VV!o*HLKECm)ZTstF8a>h{BF-(UF~5zBuP|jPeg(Pv>nu*-^o2ufqnqC-c$rM1%)r%w%1 zy<_C_7YA)Mt-qlBDY<}6;T1O}2-$R{?&{TxX}63`KKTX$h8AGQtCR9J&cvjir5avKhQB=PKO}??Veso?x`6AKmoYY@nEP3jN81AI9P-{&vA#OT5F_f!B zSc%p_Tvu0De6jgfZryBhQLXNW{tEFo7tgfE@c)a(ZmMGgG=QEAxCbZ-PzdPxN8$YS z0{{rj3cz!Kttk-#o~G0V>g7~#2iTe#8i1|}l@c*04 z{`s?j-g5q;w~Kv0i5GcmF0MQ=fA6B&%&`GKolui1?s4(;p(xhvyHBpv_l>!7lNJ)*&4KTONkXWJBAreq08|FMf($rL>6-D zLECaV^xf$+YqRY7ta>gwqUl0Q3(?9GkE-bCOhMrVy|6oXvsp5{{ZP{2z8zA;jm{fd zL!~7$w(kv7I3YodjTbc`WE%Iq(OdiT8q6{SF304LGIFqkGoKgN1-nzwNJ7VNW@$H% z?67^5bVi>6DU*Kpe|K7Fw-1}eOtDyHmVR~5okz;ak!JS$!ui7Sq*Tb<$_zXK+GNRZESFc|vi%wVok!6cC(c@X)x0?i!yHE}5M5cVg^ zwfgOUzj)xY3j8#{0{DQC0A2t@z{+G7R#FBk<0M1BegKjK6axQ&vI~d=yAD7h0B&j) z0sH|M0{Fn(3d9N22!NZMd_R5wC>Z;;xR-4}VQONKPuppSU_6MsG8JpwTz)Ij%GKT86coL&U%wEcw8Yooa~Xy{p_| z)39GwP(%*w;X0wiJL5Oqn=UK~(*5wv?TGnR=x2wG6<3FnNo+jUm`_hBmbtoKmvSv~ zePr#69cOHNcD&jfubJq;blKYz<{8G`z%J)tt+-?ZY>T8p$Z%Qb5;=;%z&EiSM)X_U z)OtUs?e_-TW5&6kDWacdR8SWIH9$K6rO6@lkHrSyVhS0+3y=~J8h{IU4}b8lmXoSb&fz0&G^p*`Q*W*P7ZzreRC=; z6|Tr-V_}s60#vGEHAeN0pxT~63<32SIfYCzh^`G$6gmqxJF8*`$WtupqB;$lm-rx4XH z)`|~8)`T&DCwPo^QlCO4N-DXEVzBF?zc=l0v|I0T zQ%`a#wX^rf_h)^7MpXFJ-Iyl|w@QhZzPx{C8qxKv6lS*3V#FWbs=1}`3KCbFu(}5Q zX2-39?~fb&F+WltF17kHpeKt%`-QwpVMmDbT|fVrx)5it+p>A|xefy@m3FFJn|07O zx1{6N_8#s*r)q^!U*y6NSJ#lmcDG($-734xsy3PiN7afU8IBccn59M{(+d428l!K{ zG;2?HE@~R>LJ;g08EveCj7l0A5zY;%k?4qYv2#QrJMI08(eCuWQBj}a@gGC$AA5Sh z4nXW5b@GpGJs=B^2zopK9WWFi3p8(_W&)aknFY)O_W}Aq zYd5);`D29-NCzPQ>ll9e7l6_JvNQ1^^Lg{-a$WCL3O9V7M)3o7de9<4b`@;FZo&Sg zK{orjMOqTzan2I6I2%iiB~oLf^dJ(Vqq1TV-qvIWtS~8&LEhs9k+eudGD%=9fimII zvGq{|J2~6F_+*kjTTZK}s6q;qTugnBJ|>30L&hv>EoQ(yxq{^HG?f}Gmlf69n;nxY zJ#ucE3W|pwIro2Ep`5dnJOmGQ zjIgMaux91P`;jWDTmp3-b=L>iXcD=jlz7FmpbE8h2zvGEo{D3+lqw4Re){I7S$nF{ z{Tb7g_dA!^ANXa4Q)s|&xkl{5@+PTs%pU%{8JANJo!;h)%ze^03j4~F;LgAfJA~E1 z@H2caj9#@4*g^NV|G;{xLqmbTxDJy~xnt51YnK!`qSVt9de}ON3ih&n84+jxOa3eJ z$V!iUiLt#EdAV3z)0T~fAk{dJMs=aT<>ok}1Oi=~(}TAz81RL6gz;3!u4S%1Mm?8+ zzUfza!OZPI$D5}k%?q)Jf2L6LX?_J%0f2xE0WOmx?7KI>W5APOiUrtB6pIxyWe#p5_Q9(~Xj~M7^Cht1Gamm6N*_1;OXZI{4pmwTZ9D*2` z6dJ;`ObcBg#A`HE5fhia+>%Ku@k0wCcD|aOBVZg{PNLQO(uBA}8oF><11<3&9kS1B z*@?Gjo-XM-*eS3w?X>D?Sl*x3dUhzHP|$mse!Ri5>E;^TsmrwTh6?+t2ki5QFOxFs zzY|d(+)Xu-_lnppSoHm2?6)O-I)h}HH4cfK<`x;(W$Z-I#WKUF?w~szc@%#e z;n%Cq3KBad3X}>&GM?__V|bY*RS`J*B1aVP>uH6wp!3z(s=CWn@;o5{{?FO`$JhtD z?SBhH_?hnW_Bxske=>x-UV!ncteVSMRQxly{FToqL-;@(CR__ordmsBr06V5WEKhf zLlVW4k|^*(5R#LbS}h^O-Ao^wn{BzrlWkwtI;jY#wp#Zcp@54$PKwDnM%p1|mYyn` zR0QF>8`VsVxpj1GfB!jy2bWiJsg-v@g1GrSnP_pZ;qS*SWPj*^`s4G47+A-zHAm&@ zt;N(5>e=$F4TtN=ro!f}lxs@G$jlIFWDXRuDDG*Md)N<0q;Ew0 z@-v&he@8dneDp)roC6Y9e34hnFf0OYyUAz%^1FJohp(e>6+jG?bFA^Du@1%681pPt z%-=0HFV?XeNzE8xKt5IhIgTnWWLMjj5~M?g(~Oj z2xd4i_NCAG=Hj}&pW8P4+ciowP!8nNEw29ADBU6(7|CYdhPezNP7aSU+uRltbZJi_ z&Idumb2u6>10keXUQ9MG#EU;Iu`rRm+soQsldLr`sS>lSY<-E0P+$o=c0BPIXp`(x z3*$w;mgI0;aEH7_Mu_a(@AzpV)l$A^vf1wYOkKU zM#aT+y}o10!pZC!A2wci?;#W{xgY4MS85}=IQD{7&x-Vzv7*Pwmj+#`Z#!z+SY&XP zd;IMuy>6a8h_k5|ob=)=uI#zWE98=rFK1~eI??-<+XJ-^TK<+{8Jw*8`sfz8 zkN@VnRC9PoVDs*i=58YI)#OpeiMjd%6%+}(_tyF6YjaWLqFl8j3k%QKLYNKyu6xQ+ z+l78@Ax2XtSWq6_z#mor;1OD;#zw5AH z5W&p1;j>%)f47m(2i?EY)q=t8`*Y+gHfi{5seA5x6|NwTyJ4DhQF0F)H0)Hg4~vqd zrI^+1Y##152iY8W2)NvvU_pFFUilgd)~Kd+c1R7 zUs@E=-?1B4WJ-7$vB4$m*CT5Zz7n{ehMnR*fAV$$9y(O=$i*UW+EZ66>FZU6@rv{P z8uJ?vYJo~|Cz;0}I}qMOHCA;^P=;5RQq`E@eovRM39}q(-p>sUY*H#jgs}|y8oyCf z0s3#nt4Aa68vd2_V2T|@=11g=J|Nw`u^No z-QwljXwL;-nV&2?*!E$L<*bGO*jw#bisCR~pgNLhQcHRaM2Dj2h{@GeF<4#kyjc`o ziViD~@Um?6F-Nj?%kV{|C5feAaYe7F(rS~jR(5z)_Obdru$*rmJb996l38<8I;&t> zGi%Eu$L{6cE0E&tFD4D`8}MM=9CFwZ`G?t60?Vtk^s`GEv4^jP1#DpX-7uNPQ< zh-_3X;9YpLxU@~z-?HsT$i`dm?dzVLyJ|7V&6R&PuN1O)JpA-PpE-$Zu0FMxa4$N><4_Qsn>D|@H)aVj#{e)|4KewOh;v?*GMHJw^7F=6$ zrSy-z6=a)r${kB&xNsEahvEai7(52{R^Q7%S4=-mc~ds~AHTZy$qA;pYv-Xa|GBy^ z=GU?FsOr20@$O}9WwZ`QdPR!R_~@Yfj-?!HOBhU>VlS53kz-I~R213V8!YY<*d+;* zi+i{*S(lP5BuQ+tIQp1lS-U;?sN&X=Q>Z{s3u5wFV~UW>;ES^xkLR71BHG##+pIh( zwx*lOX4CABHBN#k6P-GB2`(Cxz^g}$mLz)i(evNaWz2_*>QJQi|M!`&?^%Az$cKm^!PM%0kb$FuWxheB zPPyZk`!|Z|%>3ww!&T;=8}789hP$bq{Qvypq}-FIho&Fs2gk{P`FY(x-4Ax2X^$R@ z1&<^k3(icg2fgB#Mb)s`iwQ85nr#mSN@nbh+AGfF(Y!G`qY9#9_sJ=6p}JfpG}n>R z<0G>m-fmCu93ws(;^<^%#Di@@@-zyywvbmBNp10<#b-tDFUXOR_-&Wbq!|T!O@|7H zq}222bzO%&tu8+_KYsS?g~(aH&`kH2$DUPlTcHT!Tc#e(3-#s<|LMwL7#O0NUSaI`r4@RFos8^ew zP~ggjLpcWc_3>VsaBCIi+u8PfeWXCK+|7btr{9kY(J9;=Zab`QA+!{tN61DQdEJq( zWPy~Sequct&tA0PrC#iDIFgsaFOH=Z!RD379W%D@r{`c+);sxMR$`$=Up`;epD$3Q zklLJ`T}?+MW^`&m0C$w}kK-h-Z$OP;EYXD;yO2Rs$H}#O{tLtX`#iqzlOh)=Q3b=` z@kPKFH3K*F+x+AiStD}Tt_8{Ot=^<=<3`W-s0!Nc0_>(#3q(>VE;0_CK^A(`A|msn zVv@Yz_9~59Wlxcy()RAmv?lNJ#8oeC>32utL?7ygqK}yv!om|8V7eaNl`m94yD(kq&(KdRb=D& zCgt``K055?8cjPj#n7)=b_Mc6HO>fjlHOimaYixt;BZE|jmsj>=s>i>a{uF7I-hke z^9MVy#gQ<@P-%H2db)`?estc~jFHu`C;e70;r|xz>VcrxK_axW#!!*D4$j%Ydi zTuXfi8T+gv+6>+$Q!{7zE_UbM9`apUzy4cL1+IeC41DWk92y?=80Yz;1THzQ#+0-2 zd9hx4W0(5oQb?yCHcHeM*3f8j*9R}%qv8Eu9`s%R*$VquHNOMBxKHt@jrz6iS>G-PSD54r6ECzW1<{ly<7^Aj^}& zRW%gD6dVeaW8HBO(<3_>dHPnWwI}oZg}-00J2Y~oea~rpc+~03kGe1@sjX$v`2hv_ z;EPA6&vZW2yiI>sbDJE}Z@f>d<`NgEFgBeP&ZzE6<(|P^j$(nTKGOV)XG``<2GEwtUfZepi*_EQwR(JEL2i@aUm|^|LbdoW17O zm-zSe!b7)4yU`W%wNGxl=Jd}h6I~vE;NqcH7Gp6{!+xgjc`BiuY_9v0+MW#jdFuSg zfTxCfU4XabLaQ#>fBv%EWv_V(RrWCpXR&**q%XxvH`9yDzug_mN7=qwInt%6_o&vr zJOfXEB49f@jx;La=nh}!*wv0EqlRo9Az1^C<3By}{p{nPPZR3D$*Wo)*~Z!PeYn^@ z@TxWdix?_=nG=i7&ls5~UhSZKpYU2YlM~$@LJ6h{U`iE?N|rK#`QYh9gf}}PGH=p+ z41&6pK$O^|?%kQeAnpQ3@3g&{4A9ZhN}FMHOCBCml~1hRiD{G~_GUyC<-fK%?1T50S`Ek`~O3|0Dl&k0&g4HGtC&GJ!oezOz{YW?U@a-*Q@KrxZy+Lm8^ z7#?UHl)DV3?1mG`Hq}ypgE@wXay=U6uRtaf==dP6k(CBsYlmFeXZ@MukY?(z2zK(| z`~%kS-~t2=JYYEw?vMXJ-tSB3E1kmveqs-<{2RXessGOoPU*|tw)*=JhbMyhI=Z93 zxF#}~KAj5VI^;Ou4iV>B^*RP5ixw_;T!YAZxq8skM~2%T6Zn;gKvMgYe1zCdi3#z< zPy%I*Eyk8r5||;thZhyA7$PPVAFtnpVp_+rN>!Lc!on40?b}%*+QuW<^)y{uQ_1Qw z(lYEsXuxm&kDuq6D=6F| z?S31fUAa&@^wfXY5yDHPTT508sBnvIVLjBe#{oGmjW`R)70;S8{gC&(5_${W?E1Ln zv89La1;A$8NP0FxSUcMGK)(noZk32h3=iB1ew;^Wrbyac1Vu5~y4|=`KX!q@EHJ`< z&#ovT6Dn#-BBL<$U27_ncOSQB*Kbd)N)y4G`$MqV2E$Rme0!STj+F3Jnh-zG-@P-n z@+Pi^WOhV%|K5sFLF3()5UA@+)oJQj-Gu&KxTQVwh2G2mtIX?d)Vgce!-S^&(4LoV z!9nx1MN2Tt-ngl%r>|WIr%2K$ilytsejl|y6C0)a5)`2iam72*q?jxHjTq=Howe>f z72~MWY<=pcUjHb0+Qv8MO?8DUD-ZM=MVoAW?XI!-HKUJG=UYkEtTsGgeTkw}-|89Y zGr|~{#_#|G*WHF5^l?}fS#wzdp`BT}VMSS**gR*q zzh9jBeqrXH_jmt&e+N_y)+Z^0ce#DNCU`D23D^H3VkV=^DO@-4#yq7Qs OZQAa-{qlY$ literal 0 HcmV?d00001 diff --git a/plugin/vchamilo/pix/logo.png b/plugin/vchamilo/pix/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c5439f66583b76a9f23269cda2792619dcbbe5ee GIT binary patch literal 7278 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 IM6N<$f+%1EdH?_b literal 0 HcmV?d00001 diff --git a/plugin/vchamilo/plugin.php b/plugin/vchamilo/plugin.php new file mode 100644 index 000000000..bebf00af3 --- /dev/null +++ b/plugin/vchamilo/plugin.php @@ -0,0 +1,106 @@ +Plugins). + * + * @package chamilo.plugin + * + * @author Julio Montoya + */ +global $_configuration; + +/** + * Plugin details (must be present). + */ + +/* Plugin config */ + +//the plugin title +$plugin_info['title'] = 'Chamilo Virtualization'; +//the comments that go with the plugin +$plugin_info['comment'] = "Allows the virtualization of Chamilo. Use your main Chamilo installation as a hub, then create instances based on the same code but with different databases, and generate snapshots, and manage all instances like VM images."; +//the plugin version +$plugin_info['version'] = '1.2'; +//the plugin author +$plugin_info['author'] = 'Valery Fremaux, Julio Montoya'; + +/* Plugin optional settings */ + +/* + * This form will be showed in the plugin settings once the plugin was installed + * in the plugin/hello_world/index.php you can have access to the value: $plugin_info['settings']['hello_world_show_type'] +*/ + +$form = new FormValidator('vchamilo_form'); + +$plugin = VChamiloPlugin::create(); + +$form_settings = [ + 'enable_virtualisation' => Virtual::getConfig('vchamilo', 'enable_virtualisation', true), + 'httpproxyhost' => Virtual::getConfig('vchamilo', 'httpproxyhost', true), + 'httpproxyport' => Virtual::getConfig('vchamilo', 'httpproxyport', true), + 'httpproxybypass' => Virtual::getConfig('vchamilo', 'httpproxybypass', true), + 'httpproxyuser' => Virtual::getConfig('vchamilo', 'httpproxyuser', true), + 'httpproxypassword' => Virtual::getConfig('vchamilo', 'httpproxypassword', true), + 'cmd_mysql' => Virtual::getConfig('vchamilo', 'cmd_mysql', true), + 'cmd_mysqldump' => Virtual::getConfig('vchamilo', 'cmd_mysqldump', true), + 'course_real_root' => Virtual::getConfig('vchamilo', 'course_real_root', true), + 'archive_real_root' => Virtual::getConfig('vchamilo', 'archive_real_root', true), + 'home_real_root' => Virtual::getConfig('vchamilo', 'home_real_root', true), + 'upload_real_root' => Virtual::getConfig('vchamilo', 'upload_real_root', true), +]; + +$form->setDefaults($form_settings); + +$wwwroot = $_configuration['root_web']; + +//A simple select +$options = [0 => $plugin->get_lang('no'), 1 => $plugin->get_lang('yes')]; +$form->addLabel( + '', + ''. + $plugin->get_lang('manage_instances').'' +); +$form->addElement('header', $plugin->get_lang('enabling')); +$form->addElement('select', 'enable_virtualisation', $plugin->get_lang('enable_virtualisation'), $options); +$form->addElement( + 'text', + 'course_real_root', + [$plugin->get_lang('courserealroot'), 'Example: '.api_get_path(SYS_PATH).'var/courses/'] +); +$form->addElement( + 'text', + 'archive_real_root', + [$plugin->get_lang('archiverealroot'), 'Example: '.api_get_path(SYS_PATH).'var/cache/'] +); +$form->addElement( + 'text', + 'home_real_root', + [$plugin->get_lang('homerealroot'), 'Example: '.api_get_path(SYS_PATH).'var/home/'] +); + +$form->addElement( + 'text', + 'upload_real_root', + [$plugin->get_lang('UploadRealRoot'), 'Example: '.api_get_path(SYS_PATH).'var/upload/'] +); + +$form->addElement('header', $plugin->get_lang('mysqlcmds')); +$form->addElement('text', 'cmd_mysql', [$plugin->get_lang('mysqlcmd'), 'Example: /usr/bin/mysql']); +$form->addElement('text', 'cmd_mysqldump', [$plugin->get_lang('mysqldumpcmd'), 'Example: /usr/bin/mysqldump']); +$form->addElement('header', $plugin->get_lang('proxysettings')); +$form->addElement('text', 'httpproxyhost', $plugin->get_lang('httpproxyhost')); +$form->addElement('text', 'httpproxyport', $plugin->get_lang('httpproxyport')); +$form->addElement('text', 'httpproxybypass', $plugin->get_lang('httpproxybypass')); +$form->addElement('text', 'httpproxyuser', $plugin->get_lang('httpproxyuser')); +$form->addElement('text', 'httpproxypassword', $plugin->get_lang('httpproxypassword')); +$form->addButtonSave($plugin->get_lang('Save')); + +$plugin_info['settings_form'] = $form; + +// Set the templates that are going to be used +$plugin_info['templates'] = ['template.tpl']; + +$plugin_info['plugin_class'] = get_class($plugin); diff --git a/plugin/vchamilo/template.tpl b/plugin/vchamilo/template.tpl new file mode 100644 index 000000000..b96f52adf --- /dev/null +++ b/plugin/vchamilo/template.tpl @@ -0,0 +1,10 @@ +
    +

    {{title}}

    +
    +
    + +
    \ No newline at end of file diff --git a/plugin/vchamilo/templates/README.txt b/plugin/vchamilo/templates/README.txt new file mode 100644 index 000000000..35fd168cf --- /dev/null +++ b/plugin/vchamilo/templates/README.txt @@ -0,0 +1,2 @@ +This directory would be http server writable for templates snapshot to be created. +Set GID to web server group, or give write access to everyone. \ No newline at end of file diff --git a/plugin/vchamilo/uninstall.php b/plugin/vchamilo/uninstall.php new file mode 100644 index 000000000..b98643036 --- /dev/null +++ b/plugin/vchamilo/uninstall.php @@ -0,0 +1,9 @@ +ACTIVATION = 'cli'; // choose how individual cron are launched, 'cli' or 'web' +$VCRON->STRATEGY = ROUND_ROBIN; // choose vcron rotation mode +$VCRON->PERIOD = 15 * MINSECS; // used if LOWEST_POSSIBLE_GAP to setup the max gap +$VCRON->TIMEOUT = 300; // time out for CURL call to effective cron +// $VCRON->TRACE = $_configuration['root_sys'].'plugin/vchamilo/log/vcrontrace.log'; // Trace file where to collect cron outputs +$VCRON->TRACE = '/data/log/chamilo/vcrontrace.log'; // Trace file where to collect cron outputs +$VCRON->TRACE_ENABLE = true; // enables tracing + +if (!is_dir($_configuration['root_sys'].'plugin/vchamilo/log')) { + $mode = api_get_permissions_for_new_directories(); + mkdir($_configuration['root_sys'].'plugin/vchamilo/log', $mode, true); +} + +/** + * fire a cron URL using CURL. + */ +function fire_vhost_cron($vhost) +{ + global $VCRON; + + if ($VCRON->TRACE_ENABLE) { + $CRONTRACE = fopen($VCRON->TRACE, 'a'); + } + $ch = curl_init($vhost->root_web.'/main/cron/run.php'); + + $http_proxy_host = api_get_setting('vchamilo_httpproxyhost', 'vchamilo'); + $http_proxy_port = api_get_setting('vchamilo_httpproxyport', 'vchamilo'); + $http_proxy_bypass = api_get_setting('vchamilo_httpproxybypass', 'vchamilo'); + $http_proxy_user = api_get_setting('vchamilo_httpproxyuser', 'vchamilo'); + $http_proxy_password = api_get_setting('vchamilo_httpproxypassword', 'vchamilo'); + + curl_setopt($ch, CURLOPT_TIMEOUT, $VCRON->TIMEOUT); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'Chamilo'); + curl_setopt($ch, CURLOPT_POSTFIELDS, ''); + curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: text/xml charset=UTF-8"]); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + + // Check for proxy. + if (!empty($http_proxy_host) && !is_proxybypass($uri)) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false); + + if (empty($http_proxy_port)) { + echo "Using proxy $http_proxy_host\n"; + curl_setopt($ch, CURLOPT_PROXY, $http_proxy_host); + } else { + echo "Using proxy $http_proxy_host:$http_proxy_port\n"; + curl_setopt($ch, CURLOPT_PROXY, $http_proxy_host.':'.$http_proxy_port); + } + + if (!empty($http_proxy_user) and !empty($http_proxy_password)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxy_user.':'.$http_proxy_password); + if (defined('CURLOPT_PROXYAUTH')) { + // any proxy authentication if PHP 5.1 + curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM); + } + } + } + + $timestamp_send = time(); + $rawresponse = curl_exec($ch); + $timestamp_receive = time(); + + if ($rawresponse === false) { + $error = curl_errno($ch).':'.curl_error($ch); + if ($VCRON->TRACE_ENABLE) { + if ($CRONTRACE) { + fputs($CRONTRACE, "VCron start on $vhost->root_web : ".api_time_to_hms($timestamp_send)."\n"); + fputs($CRONTRACE, "VCron Error : $error \n"); + fputs($CRONTRACE, "VCron stop on $vhost->root_web : $timestamp_receive\n#################\n\n"); + fclose($CRONTRACE); + } + } + echo "VCron started on $vhost->root_web : ".api_time_to_hms($timestamp_send)."\n"; + echo "VCron Error : $error \n"; + echo "VCron stop on $vhost->root_web : ".api_time_to_hms($timestamp_receive)."\n#################\n\n"; + + return false; + } + + if ($VCRON->TRACE_ENABLE) { + if ($CRONTRACE) { + fputs($CRONTRACE, "VCron start on $vhost->vhostname : ".api_time_to_hms($timestamp_send)."\n"); + fputs($CRONTRACE, $rawresponse."\n"); + fputs($CRONTRACE, "VCron stop on $vhost->vhostname : ".api_time_to_hms($timestamp_receive)."\n#################\n\n"); + fclose($CRONTRACE); + } + } + echo "VCron start on $vhost->root_web : ".api_time_to_hms($timestamp_send)."\n"; + echo $rawresponse."\n"; + echo "VCron stop on $vhost->root_web : ".api_time_to_hms($timestamp_receive)."\n#################\n\n"; + $vhost->lastcrongap = time() - $vhost->lastcron; + $vhost->lastcron = $timestamp_send; + $vhost->croncount++; + + $vhostid = $vhost->id; + unset($vhost->id); + + Database::update('vchamilo', (array) $vhost, ['id = ?' => $vhostid]); +} + +/** + * fire a cron URL using cli exec. + */ +function exec_vhost_cron($vhost) +{ + global $VCRON, $DB, $_configuration; + + if ($VCRON->TRACE_ENABLE) { + $CRONTRACE = fopen($VCRON->TRACE, 'a'); + } + + $cmd = 'php "'.$_configuration['root_sys'].'/plugin/vchamilo/cli/cron.php" --host='.$vhost->root_web; + + $timestamp_send = time(); + exec($cmd, $rawresponse); + $timestamp_receive = time(); + + if ($VCRON->TRACE_ENABLE) { + if ($CRONTRACE) { + fputs($CRONTRACE, "VCron start on $vhost->root_web : $timestamp_send\n"); + fputs($CRONTRACE, $rawresponse."\n"); + fputs($CRONTRACE, "VCron stop on $vhost->root_web : $timestamp_receive\n#################\n\n"); + fclose($CRONTRACE); + } + } + + echo "VCron start on $vhost->root_web : $timestamp_send\n"; + echo implode("\n", $rawresponse)."\n"; + echo "VCron stop on $vhost->root_web : $timestamp_receive\n#################\n\n"; + + $vhost->lastcrongap = time() - $vhost->lastcron; + $vhost->lastcron = $timestamp_send; + $vhost->croncount++; + + $DB->update_record('vchamilo', $vhost, 'id'); +} + +/** + * check if $url matches anything in proxybypass list. + * + * any errors just result in the proxy being used (least bad) + * + * @global object + * + * @param string $url url to check + * + * @return bool true if we should bypass the proxy + */ +function is_proxybypass($url) +{ + $http_proxy_host = api_get_setting('vchamilo_httpproxyhost', 'vchamilo'); + $http_proxy_port = api_get_setting('vchamilo_httpproxyport', 'vchamilo'); + $http_proxy_bypass = api_get_setting('vchamilo_httpproxybypass', 'vchamilo'); + + // sanity check + if (empty($http_proxy_host) or empty($http_proxy_bypass)) { + return false; + } + + // get the host part out of the url + if (!$host = parse_url($url, PHP_URL_HOST)) { + return false; + } + + // get the possible bypass hosts into an array + $matches = explode(',', $http_proxy_bypass); + + // check for a match + // (IPs need to match the left hand side and hosts the right of the url, + // but we can recklessly check both as there can't be a false +ve) + $bypass = false; + foreach ($matches as $match) { + $match = trim($match); + + // try for IP match (Left side) + $lhs = substr($host, 0, strlen($match)); + if (strcasecmp($match, $lhs) == 0) { + return true; + } + + // try for host match (Right side) + $rhs = substr($host, -strlen($match)); + if (strcasecmp($match, $rhs) == 0) { + return true; + } + } + + // nothing matched. + return false; +} + +// Main execution sequence + +if (!$vchamilos = Database::select('*', 'vchamilo', [], 'all')) { + exit("Nothing to do. No Vhosts"); +} + +$allvhosts = array_values($vchamilos); + +echo "
    ";
    +echo "Chamilo VCron... start\n";
    +echo "Last croned : ".api_get_setting('vchamilo_cron_lasthost', 'vchamilo')."\n";
    +
    +if ($VCRON->STRATEGY == ROUND_ROBIN) {
    +    $rr = 0;
    +    foreach ($allvhosts as $vhostassoc) {
    +        $vhost = (object) $vhostassoc;
    +        if ($rr == 1) {
    +            api_set_setting('vchamilo_cron_lasthost', $vhost->id);
    +            echo "Round Robin : ".$vhost->root_web."\n";
    +            if ($VCRON->ACTIVATION == 'cli') {
    +                exec_vhost_cron($vhost);
    +            } else {
    +                fire_vhost_cron($vhost);
    +            }
    +
    +            exit('Done.');
    +        }
    +        if ($vhost->id == api_get_setting('vchamilo_cron_lasthost', 'vchamilo')) {
    +            $rr = 1; // take next one
    +        }
    +    }
    +
    +    // We were at last. Loop back and take first.
    +    $firsthost = (object) $allvhosts[0];
    +    api_set_setting('vchamilo_cron_lasthost', $firsthost->id, 'vchamilo');
    +    echo "Round Robin : ".$firsthost->root_web."\n";
    +    if ($VCRON->ACTIVATION == 'cli') {
    +        exec_vhost_cron($firsthost);
    +    } else {
    +        fire_vhost_cron($firsthost);
    +    }
    +} elseif ($VCRON->STRATEGY == LOWEST_POSSIBLE_GAP) {
    +    // First make measurement of cron period.
    +    if (api_get_setting('vcrontickperiod', 'vchamilo')) {
    +        api_set_setting('vcrontime', time(), 'vchamilo');
    +
    +        return;
    +    }
    +    api_set_setting('vcrontickperiod', time() - api_get_setting('vcrontime', 'vchamilo'), 'vchamilo');
    +    $hostsperturn = max(1, $VCRON->PERIOD / api_get_setting('vcrontickperiod', 'vchamilo') * count($allvhosts));
    +    $i = 0;
    +    foreach ($allvhosts as $vhostassoc) {
    +        $vhost = (object) $vhostassoc;
    +        if ((time() - $vhost->lastcron) > $VCRON->PERIOD) {
    +            if ($VCRON->ACTIVATION == 'cli') {
    +                exec_vhost_cron($vhost);
    +            } else {
    +                fire_vhost_cron($vhost);
    +            }
    +            $i++;
    +            if ($i >= $hostsperturn) {
    +                return;
    +            }
    +        }
    +    }
    +}
    diff --git a/plugin/vchamilo/views/editinstance.php b/plugin/vchamilo/views/editinstance.php
    new file mode 100644
    index 000000000..70d3d21c3
    --- /dev/null
    +++ b/plugin/vchamilo/views/editinstance.php
    @@ -0,0 +1,78 @@
    +';
    +
    +// get parameters
    +$id = isset($_REQUEST['vid']) ? $_REQUEST['vid'] : '';
    +$action = isset($_REQUEST['what']) ? $_REQUEST['what'] : '';
    +$registeronly = isset($_REQUEST['registeronly']) ? $_REQUEST['registeronly'] : 0;
    +$plugin = VChamiloPlugin::create();
    +$thisurl = api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php';
    +
    +if ($id) {
    +    $mode = 'update';
    +} else {
    +    $mode = $registeronly ? 'register' : 'add';
    +}
    +
    +$vhost = (array) Virtual::getInstance($id);
    +
    +$form = new InstanceForm($plugin, $mode, $vhost);
    +
    +if ($data = $form->get_data()) {
    +    switch ($data->what) {
    +        case 'addinstance':
    +        case 'registerinstance':
    +            Virtual::addInstance($data);
    +            echo 'Continue';
    +            exit;
    +            break;
    +        case 'updateinstance':
    +            unset($data->what);
    +            unset($data->submitbutton);
    +            unset($data->registeronly);
    +            unset($data->template);
    +            $data->lastcron = 0;
    +            $data->lastcrongap = 0;
    +            $data->croncount = 0;
    +            $id = $data->vid;
    +            unset($data->vid);
    +            unset($data->testconnection);
    +            unset($data->testdatapath);
    +            unset($data->vid);
    +
    +            Database::update('vchamilo', (array) $data, ['id = ?' => $id], false);
    +            Display::addFlash(Display::return_message(get_lang('Updated')));
    +            Virtual::redirect(api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php');
    +            break;
    +    }
    +}
    +
    +if ($id) {
    +    $vhost['vid'] = $vhost['id'];
    +    unset($vhost['id']);
    +    $form->set_data($vhost);
    +} else {
    +    $vhost['db_host'] = 'localhost';
    +    $vhost['registeronly'] = $registeronly;
    +    $form->set_data($vhost);
    +}
    +
    +$content = $form->return_form();
    +
    +$interbreadcrumb[] = ['url' => 'manage.php', 'name' => get_lang('VChamilo')];
    +
    +$tpl = new Template(get_lang('Instance'), true, true, false, true, false);
    +$tpl->assign('content', $content);
    +$tpl->display_one_col_template();
    diff --git a/plugin/vchamilo/views/editinstance_form.php b/plugin/vchamilo/views/editinstance_form.php
    new file mode 100644
    index 000000000..cdbda36a8
    --- /dev/null
    +++ b/plugin/vchamilo/views/editinstance_form.php
    @@ -0,0 +1,579 @@
    +_mode = $mode;
    +        $this->_cancelurl = $cancelurl;
    +        $this->_customdata = $customdata;
    +
    +        $attributes = ['style' => 'width: 60%; float: '.($text_dir == 'rtl' ? 'right;' : 'left;')];
    +        $this->_form = new FormValidator(
    +            $mode.'_instance',
    +            'post',
    +            $returnurl,
    +            '',
    +            $attributes
    +        );
    +    }
    +
    +    abstract public function definition();
    +
    +    abstract public function validation($data, $files = null);
    +
    +    public function validate()
    +    {
    +        return $this->_form->validate();
    +    }
    +
    +    public function display()
    +    {
    +        return $this->_form->display();
    +    }
    +
    +    public function definition_after_data()
    +    {
    +    }
    +
    +    public function return_form()
    +    {
    +        return $this->_form->toHtml();
    +    }
    +
    +    public function is_in_add_mode()
    +    {
    +        return $this->_mode == 'add';
    +    }
    +
    +    /**
    +     * Return submitted data if properly submitted or returns NULL if validation fails or
    +     * if there is no submitted data.
    +     *
    +     * @param bool $slashed true means return data with addslashes applied
    +     *
    +     * @return object submitted data; NULL if not valid or not submitted
    +     */
    +    public function get_data($slashed = true)
    +    {
    +        $cform = &$this->_form;
    +
    +        if ($this->is_submitted() and $this->is_validated()) {
    +            $data = $cform->exportValues(null, $slashed);
    +            unset($data['sesskey']); // we do not need to return sesskey
    +            if (empty($data)) {
    +                return null;
    +            } else {
    +                return (object) $data;
    +            }
    +        } else {
    +            return null;
    +        }
    +    }
    +
    +    /**
    +     * Return submitted data without validation or NULL if there is no submitted data.
    +     *
    +     * @param bool $slashed true means return data with addslashes applied
    +     *
    +     * @return object submitted data; NULL if not submitted
    +     */
    +    public function get_submitted_data($slashed = true)
    +    {
    +        $cform = &$this->_form;
    +
    +        if ($this->is_submitted()) {
    +            $data = $cform->exportValues(null, $slashed);
    +            unset($data['sesskey']); // we do not need to return sesskey
    +            if (empty($data)) {
    +                return null;
    +            } else {
    +                return (object) $data;
    +            }
    +        } else {
    +            return null;
    +        }
    +    }
    +
    +    /**
    +     * Check that form was submitted. Does not check validity of submitted data.
    +     *
    +     * @return bool true if form properly submitted
    +     */
    +    public function is_submitted()
    +    {
    +        return $this->_form->isSubmitted();
    +    }
    +
    +    /**
    +     * Return true if a cancel button has been pressed resulting in the form being submitted.
    +     *
    +     * @return bool true if a cancel button has been pressed
    +     */
    +    public function is_cancelled()
    +    {
    +        $cform = &$this->_form;
    +        if ($cform->isSubmitted()) {
    +            foreach ($cform->_cancelButtons as $cancelbutton) {
    +                if (optional_param($cancelbutton, 0, PARAM_RAW)) {
    +                    return true;
    +                }
    +            }
    +        }
    +
    +        return false;
    +    }
    +
    +    /**
    +     * Check that form data is valid.
    +     * You should almost always use this, rather than {@see validate_defined_fields}.
    +     *
    +     * @return bool true if form data valid
    +     */
    +    public function is_validated()
    +    {
    +        //finalize the form definition before any processing
    +        if (!$this->_definition_finalized) {
    +            $this->_definition_finalized = true;
    +            $this->definition_after_data();
    +        }
    +
    +        return $this->validate_defined_fields();
    +    }
    +
    +    /**
    +     * Validate the form.
    +     *
    +     * You almost always want to call {@see is_validated} instead of this
    +     * because it calls {@see definition_after_data} first, before validating the form,
    +     * which is what you want in 99% of cases.
    +     *
    +     * This is provided as a separate function for those special cases where
    +     * you want the form validated before definition_after_data is called
    +     * for example, to selectively add new elements depending on a no_submit_button press,
    +     * but only when the form is valid when the no_submit_button is pressed,
    +     *
    +     * @param bool $validateonnosubmit optional, defaults to false.  The default behaviour
    +     *                                 is NOT to validate the form when a no submit button has been pressed.
    +     *                                 pass true here to override this behaviour
    +     *
    +     * @return bool true if form data valid
    +     */
    +    public function validate_defined_fields($validateonnosubmit = false)
    +    {
    +        static $validated = null; // one validation is enough
    +        $cform = &$this->_form;
    +
    +        if ($this->no_submit_button_pressed() && empty($validateonnosubmit)) {
    +            return false;
    +        } elseif ($validated === null) {
    +            $internal_val = $cform->validate();
    +
    +            $files = [];
    +            $file_val = $this->_validate_files($files);
    +            if ($file_val !== true) {
    +                if (!empty($file_val)) {
    +                    foreach ($file_val as $element => $msg) {
    +                        $cform->setElementError($element, $msg);
    +                    }
    +                }
    +                $file_val = false;
    +            }
    +
    +            $data = $cform->exportValues(null, true);
    +            $chamilo_val = $this->validation($data, $files);
    +            if ((is_array($chamilo_val) && count($chamilo_val) !== 0)) {
    +                // non-empty array means errors
    +                foreach ($chamilo_val as $element => $msg) {
    +                    $cform->setElementError($element, $msg);
    +                }
    +                $chamilo_val = false;
    +            } else {
    +                // anything else means validation ok
    +                $chamilo_val = true;
    +            }
    +
    +            $validated = ($internal_val && $chamilo_val && $file_val);
    +        }
    +
    +        return $validated;
    +    }
    +
    +    public function no_submit_button_pressed()
    +    {
    +        static $nosubmit = null; // one check is enough
    +
    +        if (!is_null($nosubmit)) {
    +            return $nosubmit;
    +        }
    +
    +        $cform = &$this->_form;
    +        $nosubmit = false;
    +        if (!$this->is_submitted()) {
    +            return false;
    +        }
    +
    +        /*
    +        foreach ($cform->_noSubmitButtons as $nosubmitbutton){
    +            if (optional_param($nosubmitbutton, 0, PARAM_RAW)){
    +                $nosubmit = true;
    +                break;
    +            }
    +        }
    +        return $nosubmit;
    +        */
    +        return false;
    +    }
    +
    +    /**
    +     * Load in existing data as form defaults. Usually new entry defaults are stored directly in
    +     * form definition (new entry form); this function is used to load in data where values
    +     * already exist and data is being edited (edit entry form).
    +     *
    +     * @param mixed $default_values object or array of default values
    +     * @param bool  $slashed        true if magic quotes applied to data values
    +     */
    +    public function set_data($default_values, $slashed = false)
    +    {
    +        if (is_object($default_values)) {
    +            $default_values = (array) $default_values;
    +        }
    +        $filter = $slashed ? 'stripslashes' : null;
    +        $this->_form->setDefaults($default_values, $filter);
    +    }
    +
    +    /**
    +     * Internal method. Validates all uploaded files.
    +     */
    +    public function _validate_files(&$files)
    +    {
    +        $files = [];
    +
    +        if (empty($_FILES)) {
    +            // we do not need to do any checks because no files were submitted
    +            // note: server side rules do not work for files - use custom verification in validate() instead
    +            return true;
    +        }
    +
    +        $errors = [];
    +        $mform = &$this->_form;
    +
    +        // check the files
    +        $status = $this->_upload_manager->preprocess_files();
    +
    +        // now check that we really want each file
    +        foreach ($_FILES as $elname => $file) {
    +            if ($mform->elementExists($elname) and $mform->getElementType($elname) == 'file') {
    +                $required = $mform->isElementRequired($elname);
    +                if (!empty($this->_upload_manager->files[$elname]['uploadlog']) &&
    +                    empty($this->_upload_manager->files[$elname]['clear'])
    +                ) {
    +                    if (!$required and $file['error'] == UPLOAD_ERR_NO_FILE) {
    +                        // file not uploaded and not required - ignore it
    +                        continue;
    +                    }
    +                    $errors[$elname] = $this->_upload_manager->files[$elname]['uploadlog'];
    +                } elseif (!empty($this->_upload_manager->files[$elname]['clear'])) {
    +                    $files[$elname] = $this->_upload_manager->files[$elname]['tmp_name'];
    +                }
    +            } else {
    +                error('Incorrect upload attempt!');
    +            }
    +        }
    +
    +        // return errors if found
    +        if ($status && 0 == count($errors)) {
    +            return true;
    +        } else {
    +            $files = [];
    +
    +            return $errors;
    +        }
    +    }
    +}
    +
    +/**
    + * Class InstanceForm.
    + */
    +class InstanceForm extends ChamiloForm
    +{
    +    /** @var Plugin */
    +    public $_plugin;
    +    public $instance;
    +
    +    /**
    +     * InstanceForm constructor.
    +     *
    +     * @param $plugin
    +     * @param string $mode
    +     */
    +    public function __construct($plugin, $mode = 'add', $instance = [])
    +    {
    +        global $_configuration;
    +
    +        $this->_plugin = $plugin;
    +        $returnUrl = $_configuration['root_web'].'plugin/vchamilo/views/editinstance.php';
    +        if ($mode == 'update') {
    +            $returnUrl = $_configuration['root_web'].'plugin/vchamilo/views/editinstance.php?vid='.intval($_GET['vid']);
    +        }
    +
    +        $cancelurl = $_configuration['root_web'].'plugin/vchamilo/views/manage.php';
    +        parent::__construct($mode, $returnUrl, $cancelurl);
    +        $this->instance = $instance;
    +        $this->definition();
    +    }
    +
    +    public function definition()
    +    {
    +        global $_configuration;
    +
    +        $form = $this->_form;
    +        $plugin = $this->_plugin;
    +
    +        $form->addElement('hidden', 'vid');
    +        $form->addElement('hidden', 'what', $this->_mode.'instance');
    +        $form->addElement('hidden', 'registeronly');
    +
    +        $form->addHeader($plugin->get_lang('hostdefinition'));
    +        $form->addText(
    +            'sitename',
    +            [
    +                $plugin->get_lang('sitename'),
    +                $plugin->get_lang('SiteNameExample'),
    +            ]
    +        );
    +        $form->applyFilter('sitename', 'trim');
    +        $form->addText(
    +            'institution',
    +            [
    +                $plugin->get_lang('institution'),
    +                $plugin->get_lang('InstitutionExample'),
    +            ]
    +        );
    +        $form->applyFilter('institution', 'trim');
    +
    +        // Host's name.
    +        $elementWeb = $form->addElement(
    +            'text',
    +            'root_web',
    +            [$this->_plugin->get_lang('rootweb'), $plugin->get_lang('RootWebExample')]
    +        );
    +        $form->applyFilter('root_web', 'trim');
    +
    +        $form->addElement(
    +            'text',
    +            'url_append',
    +            ['url_append', $plugin->get_lang('UrlAppendExample')]
    +        );
    +
    +        if ($this->_mode == 'update') {
    +            $encryptList = Virtual::getEncryptList();
    +            $encryptMethod = $form->addElement(
    +                'select',
    +                'password_encryption',
    +                get_lang('EncryptMethodUserPass'),
    +                $encryptList
    +            );
    +            $encryptMethod->freeze();
    +            $elementWeb->freeze();
    +        }
    +
    +        /*
    +         * Database fieldset.
    +         */
    +        $form->addElement('header', $plugin->get_lang('dbgroup'));
    +
    +        // Database host.
    +        $form->addElement(
    +            'text',
    +            'db_host',
    +            $this->_plugin->get_lang('dbhost'),
    +            ['id' => 'id_vdbhost']
    +        );
    +        $form->applyFilter('db_host', 'trim');
    +
    +        // Database login.
    +        $form->addElement(
    +            'text',
    +            'db_user',
    +            $this->_plugin->get_lang('dbuser'),
    +            ['id' => 'id_vdbuser']
    +        );
    +        $form->applyFilter('db_user', 'trim');
    +
    +        // Database password.
    +        $form->addElement(
    +            'password',
    +            'db_password',
    +            $this->_plugin->get_lang('dbpassword'),
    +            ['id' => 'id_vdbpassword']
    +        );
    +
    +        // Database name.
    +        $form->addText(
    +            'main_database',
    +            [
    +                $plugin->get_lang('maindatabase'),
    +                $plugin->get_lang('DatabaseDescription'),
    +            ]
    +        );
    +
    +        // Button for testing database connection.
    +        $form->addElement(
    +            'button',
    +            'testconnection',
    +            $this->_plugin->get_lang('testconnection'),
    +            'check',
    +            'default',
    +            'default',
    +            '',
    +            'onclick="opencnxpopup(\''.$_configuration['root_web'].'\'); return false;"'
    +        );
    +
    +        $form->addText('archive_url', $this->_plugin->get_lang('ArchiveUrl'));
    +        $form->addText('home_url', $this->_plugin->get_lang('HomeUrl'));
    +        $form->addText('upload_url', $this->_plugin->get_lang('UploadUrl'));
    +        $form->addText(
    +            'css_theme_folder',
    +            [
    +                $this->_plugin->get_lang('ThemeFolder'),
    +                $this->_plugin->get_lang('ThemeFolderExplanation'),
    +            ],
    +            false
    +        );
    +        //$form->addText('course_url', $this->_plugin->get_lang('CourseUrl'));
    +
    +        /**
    +         * Template selection.
    +         */
    +        if ($this->is_in_add_mode()) {
    +            $form->addElement('header', $this->_plugin->get_lang('templating'));
    +
    +            $templateoptions = Virtual::getAvailableTemplates();
    +
    +            // Template choice
    +            $form->addSelect(
    +                'template',
    +                $this->_plugin->get_lang('template'),
    +                $templateoptions
    +            );
    +        } else {
    +            if ($this->instance) {
    +                $form->addLabel(
    +                    'slug',
    +                    $this->instance['slug']
    +                );
    +
    +                $form->addLabel(
    +                    'archive_real_root',
    +                    api_add_trailing_slash(Virtual::getConfig('vchamilo', 'archive_real_root')).
    +                    $this->instance['slug']
    +                );
    +
    +                $form->addLabel(
    +                    'course_real_root',
    +                    api_add_trailing_slash(Virtual::getConfig('vchamilo', 'course_real_root')).
    +                        $this->instance['slug']
    +                );
    +
    +                $form->addLabel(
    +                    'home_real_root',
    +                    api_add_trailing_slash(Virtual::getConfig('vchamilo', 'home_real_root')).$this->instance['slug']
    +                );
    +
    +                $form->addLabel(
    +                    'upload_real_root',
    +                    api_add_trailing_slash(Virtual::getConfig('vchamilo', 'upload_real_root')).$this->instance['slug']
    +                );
    +
    +                $form->addLabel(
    +                    $this->_plugin->get_lang('template'),
    +                    $this->instance['template']
    +                );
    +            }
    +        }
    +
    +        $form->addButtonSave(
    +            $this->_plugin->get_lang('savechanges'),
    +            'submitbutton'
    +        );
    +
    +        // Rules
    +        $form->addRule(
    +            'sitename',
    +            $this->_plugin->get_lang('sitenameinputerror'),
    +            'required',
    +            null,
    +            'client'
    +        );
    +        $form->addRule(
    +            'institution',
    +            $this->_plugin->get_lang('institutioninputerror'),
    +            'required',
    +            null,
    +            'client'
    +        );
    +
    +        $form->addRule(
    +            'root_web',
    +            $this->_plugin->get_lang('rootwebinputerror'),
    +            'required',
    +            null,
    +            'client'
    +        );
    +        $form->addRule(
    +            'main_database',
    +            $this->_plugin->get_lang('databaseinputerror'),
    +            'required',
    +            null,
    +            'client'
    +        );
    +    }
    +
    +    /**
    +     * @param array $data
    +     * @param null  $files
    +     *
    +     * @return array
    +     */
    +    public function validation($data, $files = null)
    +    {
    +        global $plugin;
    +
    +        $errors = [];
    +        $tablename = Database::get_main_table('vchamilo');
    +        $vchamilo = Database::select(
    +            '*',
    +            $tablename,
    +            ['where' => [' root_web = ? ' => [$data['root_web']]]],
    +            'first'
    +        );
    +
    +        if ($vchamilo && isset($data['vid']) && $data['vid'] != $vchamilo['id']) {
    +            $errors['root_web'] = $plugin->get_lang('RootWebExists');
    +        }
    +
    +        if (!empty($errors)) {
    +            return $errors;
    +        }
    +    }
    +}
    diff --git a/plugin/vchamilo/views/import.php b/plugin/vchamilo/views/import.php
    new file mode 100644
    index 000000000..91143dd72
    --- /dev/null
    +++ b/plugin/vchamilo/views/import.php
    @@ -0,0 +1,196 @@
    + 'manage.php', 'name' => get_lang('VChamilo')];
    +
    +// Security
    +api_protect_admin_script();
    +
    +Virtual::checkSettings();
    +
    +$plugin = VChamiloPlugin::create();
    +$form = new FormValidator('import', 'post', api_get_self());
    +
    +// Database host.
    +$form->addHeader(get_lang('From'));
    +
    +$form->addText('root_web', [$plugin->get_lang('rootweb'), 'Example: http://www.chamilo.org/']);
    +$form->addText('db_host', $plugin->get_lang('dbhost'));
    +$form->applyFilter('db_host', 'trim');
    +
    +// Database login.
    +$form->addText('db_user', $plugin->get_lang('dbuser'));
    +$form->applyFilter('db_user', 'trim');
    +
    +// Database password.
    +$form->addElement(
    +    'password',
    +    'db_password',
    +    $plugin->get_lang('dbpassword'),
    +    ['id' => 'id_vdbpassword']
    +);
    +
    +// Database name.
    +$form->addText('main_database', [$plugin->get_lang('maindatabase')]);
    +
    +$form->addText(
    +    'configuration_file',
    +    [
    +        $plugin->get_lang('ConfigurationPath'),
    +        get_lang('Example').': /var/www/site/app/config/configuration.php',
    +    ],
    +    true
    +);
    +
    +$encryptList = Virtual::getEncryptList();
    +
    +$form->addSelect(
    +    'password_encryption',
    +    get_lang('EncryptMethodUserPass'),
    +    $encryptList
    +);
    +
    +$encryptList = Virtual::getEncryptList();
    +
    +$versionList = [
    +    '1.11.x',
    +    '1.10.x',
    +    '1.9.x',
    +];
    +
    +$form->addSelect(
    +    'version',
    +    $plugin->get_lang('FromVersion'),
    +    array_combine($versionList, $versionList)
    +);
    +
    +$form->addText(
    +    'course_path',
    +    [
    +        $plugin->get_lang('CoursePath'),
    +        get_lang('Example').': /var/www/site/virtual/var/courses',
    +    ],
    +    true
    +);
    +
    +$form->addText(
    +    'home_path',
    +    [
    +        $plugin->get_lang('HomePath'),
    +        get_lang('Example').': /var/www/site/virtual/var/home',
    +    ],
    +    true
    +);
    +
    +$form->addText(
    +    'upload_path',
    +    [
    +        $plugin->get_lang('UploadPath'),
    +        get_lang('Example').': /var/www/site/virtual/var/upload',
    +    ],
    +    true
    +);
    +
    +$form->addHeader(get_lang('To'));
    +
    +$form->addText('to_db_host', $plugin->get_lang('dbhost'));
    +$form->applyFilter('to_db_host', 'trim');
    +
    +// Database login.
    +$form->addText('to_db_user', $plugin->get_lang('dbuser'));
    +$form->applyFilter('to_db_user', 'trim');
    +
    +// Database password.
    +$form->addElement(
    +    'password',
    +    'to_db_password',
    +    $plugin->get_lang('dbpassword'),
    +    ['id' => 'id_vdbpassword']
    +);
    +
    +// Database name.
    +$form->addText(
    +    'to_main_database',
    +    [
    +        $plugin->get_lang('maindatabase'),
    +        $plugin->get_lang('DatabaseDescription'),
    +    ]
    +);
    +
    +$form->addButtonSave($plugin->get_lang('savechanges'), 'submitbutton');
    +$content = $form->returnForm();
    +
    +if ($form->validate()) {
    +    $values = $form->getSubmitValues();
    +
    +    $coursePath = $values['course_path'];
    +    $homePath = $values['home_path'];
    +    $confFile = $values['configuration_file'];
    +    $uploadPath = $values['upload_path'];
    +
    +    $isPharFile = str_starts_with(strtolower($confFile), 'phar://')
    +        || str_starts_with(strtolower($coursePath), 'phar://')
    +        || str_starts_with(strtolower($homePath), 'phar://')
    +        || str_starts_with(strtolower($uploadPath), 'phar://');
    +
    +    if ($isPharFile) {
    +        Display::addFlash(
    +            Display::return_message(
    +                $plugin->get_lang('NotAllowed'),
    +                'error'
    +            )
    +        );
    +    } else {
    +        $isWritable = is_dir($coursePath)
    +            && is_dir($homePath)
    +            && is_dir($uploadPath)
    +            && file_exists($confFile)
    +            && is_readable($confFile);
    +
    +        if ($isWritable) {
    +            $currentHost = api_get_configuration_value('db_host');
    +            $currentDatabase = api_get_configuration_value('main_database');
    +            $currentUser = api_get_configuration_value('db_user');
    +            $currentPassword = api_get_configuration_value('db_password');
    +
    +            if ($values['to_main_database'] !== $currentDatabase &&
    +                $values['to_db_user'] !== $currentUser &&
    +                $values['to_db_password'] !== $currentPassword
    +            ) {
    +            } else {
    +                Display::addFlash(
    +                    Display::return_message(
    +                        $plugin->get_lang('DatabaseAccessShouldBeDifferentThanMasterChamilo'),
    +                        'warning'
    +                    )
    +                );
    +            }
    +
    +            $vchamilo = new stdClass();
    +            $vchamilo->main_database = $values['main_database'];
    +            $vchamilo->db_user = $values['db_user'];
    +            $vchamilo->db_password = $values['db_password'];
    +            $vchamilo->db_host = $values['db_host'];
    +            $vchamilo->root_web = $values['root_web'];
    +            $vchamilo->import_to_main_database = $values['to_main_database'];
    +            $vchamilo->import_to_db_user = $values['to_db_user'];
    +            $vchamilo->import_to_db_password = $values['to_db_password'];
    +            $vchamilo->import_to_db_host = $values['to_db_host'];
    +            $vchamilo->course_path = $values['course_path'];
    +            $vchamilo->home_path = $values['home_path'];
    +            $vchamilo->upload_path = $values['upload_path'];
    +            $vchamilo->password_encryption = $values['password_encryption'];
    +
    +            Virtual::importInstance($vchamilo, $values['version']);
    +
    +            Virtual::redirect(api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php');
    +        }
    +    }
    +}
    +
    +$tpl = new Template(get_lang('Import'), true, true, false, true, false);
    +$tpl->assign('content', $content);
    +$tpl->display_one_col_template();
    diff --git a/plugin/vchamilo/views/manage.controller.php b/plugin/vchamilo/views/manage.controller.php
    new file mode 100644
    index 000000000..00052587a
    --- /dev/null
    +++ b/plugin/vchamilo/views/manage.controller.php
    @@ -0,0 +1,373 @@
    + ["id IN ('$vidlist')" => []]]);
    +            }
    +        } else {
    +            $todelete = Database::select(
    +                '*',
    +                'vchamilo',
    +                ['where' => ["root_web = '{$n->root_web}' " => []]]
    +            );
    +        }
    +
    +        if ($todelete) {
    +            foreach ($todelete as $fooid => $instance) {
    +                $slug = $instance['slug'];
    +
    +                if (!empty($slug)) {
    +                    // Remove all files and eventual symlinks
    +                    $absalternatecourse = Virtual::getConfig('vchamilo', 'course_real_root');
    +                    $coursedir = $absalternatecourse.$slug;
    +
    +                    Display::addFlash(Display::return_message("Deleting $coursedir"));
    +
    +                    removeDir($coursedir);
    +
    +                    if ($absalternatehome = Virtual::getConfig('vchamilo', 'home_real_root')) {
    +                        $homedir = $absalternatehome.'/'.$slug;
    +
    +                        Display::addFlash(Display::return_message("Deleting $homedir"));
    +                        removeDir($homedir);
    +                    }
    +
    +                    // delete archive
    +                    if ($absalternatearchive = Virtual::getConfig('vchamilo', 'archive_real_root')) {
    +                        $archivedir = $absalternatearchive.'/'.$slug;
    +
    +                        Display::addFlash(Display::return_message("Deleting $archivedir"));
    +                        removeDir($archivedir);
    +                    }
    +
    +                    // Delete upload
    +                    if ($dir = Virtual::getConfig('vchamilo', 'upload_real_root')) {
    +                        $dir = $dir.'/'.$slug;
    +
    +                        Display::addFlash(Display::return_message("Deleting $dir"));
    +                        removeDir($dir);
    +                    }
    +                }
    +
    +                $sql = "DELETE FROM {$table} WHERE id = ".$instance['id'];
    +                Database::query($sql);
    +
    +                Display::addFlash(Display::return_message("Removing instance: ".$instance['root_web']));
    +
    +                Virtual::dropDatabase((object) $instance);
    +            }
    +        }
    +        break;
    +    case 'snapshotinstance':
    +        $interbreadcrumb[] = ['url' => 'manage.php', 'name' => get_lang('VChamilo')];
    +
    +        $vid = isset($_REQUEST['vid']) ? $_REQUEST['vid'] : '';
    +        if ($vid) {
    +            $vhosts = Database::select('*', 'vchamilo', ['where' => ['id = ?' => $vid]]);
    +            $vhost = (object) array_pop($vhosts);
    +        } else {
    +            $vhost = (object) $_configuration;
    +            $vhost->slug = Virtual::getSlugFromUrl($vhost->root_web);
    +            $vhost->id = 0;
    +        }
    +
    +        // Parsing url for building the template name.
    +        $wwwroot = $vhost->root_web;
    +        $vchamilostep = isset($_REQUEST['step']) ? $_REQUEST['step'] : '';
    +
    +        // Make template directory (files and SQL).
    +        $separator = DIRECTORY_SEPARATOR;
    +        $dirMode = api_get_permissions_for_new_directories();
    +
    +        $backupDir = api_get_path(SYS_PATH).'plugin'.$separator.'vchamilo'.$separator.'templates'.$separator.$vhost->slug.$separator;
    +
    +        $absolute_datadir = $backupDir.'data';
    +        $absolute_sqldir = $backupDir.'dump.sql';
    +
    +        if (!is_dir($backupDir)) {
    +            $result = mkdir($backupDir, $dirMode, true);
    +            if ($result) {
    +                Display::addFlash(
    +                    Display::return_message('Directory created: '.$backupDir)
    +                );
    +            } else {
    +                Display::addFlash(
    +                    Display::return_message("Cannot create directory: $backupDir check the folder permissions", 'error')
    +                );
    +            }
    +        }
    +
    +        if ($vchamilostep == 0) {
    +            // Create directories, if necessary.
    +            if (!is_dir($absolute_datadir)) {
    +                mkdir($absolute_datadir, $dirMode, true);
    +                mkdir($absolute_datadir.'/home', $dirMode, true);
    +            }
    +
    +            if (empty($fullautomation)) {
    +                $actionurl = $_configuration['root_web'].'/plugin/vchamilo/views/manage.php';
    +                $content = '';
    +                $content .= '';
    +                $content .= '';
    +                $content .= '';
    +                $content .= '';
    +                $content .= '';
    +                $content .= '';
    +
    +                $tpl = new Template(get_lang('Snapshot'), true, true, false, true, false);
    +                $tpl->assign('message', '

    '.$plugin->get_lang('vchamilosnapshot1').'

    '); + $tpl->assign('content', $content); + $tpl->display_one_col_template(); + + exit; + } else { + // continue next step + $vchamilostep = 1; + } + } + + if ($vchamilostep >= 1) { + if ($wwwroot == $_configuration['root_web']) { + // Make fake Vchamilo record. + $vchamilo = Virtual::makeThis(); + $coursePath = api_get_path(SYS_COURSE_PATH); + $homePath = api_get_path(SYS_HOME_PATH); + $archivePath = api_get_path(SYS_ARCHIVE_PATH); + $uploadPath = api_get_path(SYS_UPLOAD_PATH); + } else { + // Get Vchamilo known record. + $vchamilo = Database::select('*', 'vchamilo', ['where' => ['root_web = ?' => [$wwwroot]]], 'first'); + $vchamilo = (object) $vchamilo; + $coursePath = Virtual::getConfig('vchamilo', 'course_real_root'); + $homePath = Virtual::getConfig('vchamilo', 'home_real_root'); + $archivePath = Virtual::getConfig('vchamilo', 'archive_real_root'); + $uploadPath = Virtual::getConfig('vchamilo', 'upload_real_root'); + + $coursePath = $coursePath.'/'.$vchamilo->slug; + $homePath = $homePath.'/'.$vchamilo->slug; + $archivePath = $archivePath.'/'.$vchamilo->slug; + $uploadPath = $uploadPath.'/'.$vchamilo->slug; + } + + $content = ''; + if ($vchamilostep == 1) { + // Auto dump the databases in a master template folder. + // this will create three files : dump.sql + $result = Virtual::backupDatabase($vchamilo, $absolute_sqldir); + + if (empty($fullautomation)) { + if (!$result) { + $actionurl = $_configuration['root_web'].'/plugin/vchamilo/views/manage.php'; + $content .= '

    '; + $content .= ''; + $content .= '

    '; + } else { + $actionurl = $_configuration['root_web'].'/plugin/vchamilo/views/manage.php'; + + $message = $plugin->get_lang('vchamilosnapshot2'); + + Display::addFlash( + Display::return_message('Database file created: '.$absolute_sqldir) + ); + + $content .= '
    '; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= ''; + $content .= '
    '; + } + + $tpl = new Template(get_lang('Snapshot'), true, true, false, true, false); + $tpl->assign('message', '

    '.$message.'

    '); + $tpl->assign('content', $content); + $tpl->display_one_col_template(); + + exit; + } + } + + Display::addFlash(Display::return_message("Copying from '$homePath' to '{$absolute_datadir}/home' ")); + copyDirTo($homePath, $absolute_datadir.'/home/', false); + + Display::addFlash(Display::return_message("Copying from '$coursePath' to '$absolute_datadir/courses' ")); + copyDirTo($coursePath, $absolute_datadir.'/courses/', false); + + Display::addFlash(Display::return_message("Copying from '$uploadPath' to '$absolute_datadir/upload' ")); + copyDirTo($uploadPath, $absolute_datadir.'/upload/', false); + + // Store original hostname and some config info for further database or filestore replacements. + $FILE = fopen($backupDir.$separator.'manifest.php', 'w'); + fwrite($FILE, '<'.'?php '); + fwrite($FILE, "\$templatewwwroot = '".$wwwroot."';\n"); + fwrite($FILE, '?'.'>'); + fclose($FILE); + + // Every step was SUCCESS. + if (empty($fullautomation)) { + Display::addFlash( + Display::return_message( + $plugin->get_lang('successfinishedcapture'), + 'success' + ) + ); + + if (empty($vid)) { + $template = Virtual::getConfig('vchamilo', 'default_template'); + if (empty($template)) { + Display::addFlash( + Display::return_message('Set default template as '.$vhost->slug.'', 'success', false) + ); + $params = [ + 'subkey' => 'vchamilo', + 'title' => 'default_template', + 'type' => 'setting', + 'category' => 'Plugins', + 'variable' => 'vchamilo_default_template', + 'selected_value' => $vhost->slug, + 'access_url_changeable' => 0, + ]; + api_set_setting_simple($params); + } else { + Display::addFlash( + Display::return_message('Default template is: '.$vhost->slug.'', 'success', false) + ); + } + } + + $actionurl = $_configuration['root_web'].'/plugin/vchamilo/views/manage.php'; + $content .= '
    '; + $content .= ''; + $content .= '
    '; + + $tpl = new Template(get_lang('Snapshot'), true, true, false, true, false); + $tpl->assign('message', $plugin->get_lang('vchamilosnapshot3')); + $tpl->assign('content', $content); + $tpl->display_one_col_template(); + + exit; + } + } + break; + case 'clearcache': + // Removes cache directory. + if (empty($automation)) { + if (array_key_exists('vids', $_REQUEST)) { + $toclear = Database::select('*', 'vchamilo', ['where' => ["id IN ('$vidlist')" => []]]); + } else { + $vid = isset($_REQUEST['vid']) ? $_REQUEST['vid'] : 0; + if ($vid) { + $vhosts = Database::select('*', 'vchamilo', ['where' => ['id = ?' => $vid]]); + $vhost = (object) array_pop($vhosts); + $toclear[$vhost->id] = $vhost; + } else { + $toclear[0] = (object) $_configuration; + } + } + } else { + $toclear = Database::select( + '*', + 'vchamilo', + ['where' => ["root_web = '{$n->root_web}' " => []]] + ); + } + + foreach ($toclear as $fooid => $instance) { + if ($fooid == 0) { + $templatepath = api_get_path(SYS_ARCHIVE_PATH).'twig'; + Display::addFlash(Display::return_message("Deleting master cache $templatepath \n")); + removeDir($templatepath); + } else { + $coursePath = Virtual::getConfig('vchamilo', 'course_real_root'); + $homePath = Virtual::getConfig('vchamilo', 'home_real_root'); + $archivePath = Virtual::getConfig('vchamilo', 'archive_real_root'); + //$uploadPath = Virtual::getConfig('vchamilo', 'upload_real_root'); + + // Get instance archive + $archivepath = api_get_path(SYS_ARCHIVE_PATH, (array) $instance); + $templatepath = $archivePath.'/'.$instance['slug'].'/twig'; + Display::addFlash(Display::return_message("Deleting cache $templatepath \n")); + removeDir($templatepath); + } + } + break; + case 'setconfigvalue': + $select = ''; + + if (empty($vidlist)) { + api_not_allowed(true, 'No virtual chamilo selected'); + } + + Display::display_header(); + echo '

    '.$plugin->get_lang('sendconfigvalue').'

    '; + echo '
    '; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
    '.get_lang('variable').''.get_lang('subkey').'
    '.$select.'
    '; + echo '
    '; + echo '
    '; + Display::display_footer(); + exit; + break; +} diff --git a/plugin/vchamilo/views/manage.php b/plugin/vchamilo/views/manage.php new file mode 100644 index 000000000..07706e805 --- /dev/null +++ b/plugin/vchamilo/views/manage.php @@ -0,0 +1,157 @@ +id] = $instance; +} + +$templates = Virtual::getAvailableTemplates(); + +if (empty($templates)) { + $url = api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/manage.php?what=snapshotinstance'; + $url = Display::url($url, $url); + Display::addFlash( + Display::return_message('You need to create a snapshot of master first here:'.$url, 'info', false) + ); +} + +$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']); +$column = 0; +$row = 0; + +// $table->set_additional_parameters($parameters); +$headers = [ + '', + $plugin->get_lang('sitename'), + $plugin->get_lang('dbhost').' - '.get_lang('Database'), + $plugin->get_lang('coursefolder'), + $plugin->get_lang('enabled'), + $plugin->get_lang('lastcron'), + '', +]; +$attrs = ['center' => 'left']; +$table->addRow($headers, $attrs, 'th'); + +$i = 0; +foreach ($instances as $instance) { + $checkbox = ''; + $sitelink = $instance->sitename; + + if ($instance->visible) { + $status = ' + '.Display::returnFontAwesomeIcon('toggle-on', 2).''; + } else { + $status = ' + '.Display::returnFontAwesomeIcon('toggle-off', 2).''; + } + + $cmd = '  + '.Display::returnFontAwesomeIcon('pencil', 2).''; + $cmd .= '  + '.Display::returnFontAwesomeIcon('camera', 2).''; + + $cmd .= ' +  '.Display::returnFontAwesomeIcon('wrench', 2).' '; + + if (!$instance->visible) { + $cmd .= ' +  '.Display::returnFontAwesomeIcon('remove', 2).' '; + } else { + $cmd .= ' +  '.Display::returnFontAwesomeIcon('remove', 2).' '; + } + + $crondate = $instance->lastcron ? date('r', $instance->lastcron) : ''; + $data = [ + $checkbox, + $sitelink.' '.$instance->institution.' ('.Display::url($instance->root_web, $instance->root_web, ['target' => '_blank']).')', + $instance->db_host.' - '.$instance->main_database, + $instance->slug, + $status, + $crondate, + $cmd, + ]; + $attrs = ['center' => 'left']; + $table->addRow($data, $attrs, 'td'); + $i++; +} + +$items = [ + [ + 'url' => $thisurl.'?what=newinstance', + 'content' => $plugin->get_lang('newinstance'), + ], + [ + 'url' => $thisurl.'?what=import', + 'content' => $plugin->get_lang('ImportInstance'), + ], + [ + 'url' => $thisurl.'?what=snapshotinstance&vid=0', + 'content' => $plugin->get_lang('snapshotmaster'), + ], + [ + 'url' => $thisurl.'?what=clearcache&vid=0', + 'content' => $plugin->get_lang('clearmastercache'), + ], + [ + 'url' => api_get_path(WEB_PLUGIN_PATH).'vchamilo/views/syncparams.php', + 'content' => $plugin->get_lang('sync_settings'), + ], + [ + 'url' => api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=vchamilo', + 'content' => get_lang('Settings'), + ], +]; + +$content = Display::page_header('VChamilo Instances'); + +$content .= Display::actions($items); +$content .= '
    '; +$content .= $table->toHtml(); + +$selectionoptions = ['']; +$selectionoptions[] = ''; +$selectionoptions[] = ''; +$selectionoptions[] = ''; +$selectionoptions[] = ''; +$selectionoptions[] = ''; +$selectionaction = ''; + +$content .= '
    +'.$plugin->get_lang('selectall').' - +'.$plugin->get_lang('selectnone').' - +  - '.$plugin->get_lang('withselection').' '.$selectionaction.'
    '; + +$content .= '
    '; + +if (empty($templates)) { + $content = ''; +} + +$tpl = new Template(get_lang('VChamilo'), true, true, false, true, false); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/vchamilo/views/manage.testcnx.php b/plugin/vchamilo/views/manage.testcnx.php new file mode 100644 index 000000000..489d61fbe --- /dev/null +++ b/plugin/vchamilo/views/manage.testcnx.php @@ -0,0 +1,55 @@ + 'pdo_mysql', + 'host' => $_REQUEST['vdbhost'], + 'user' => $_REQUEST['vdblogin'], + 'password' => $_REQUEST['vdbpass'], + //'dbname' => isset($_configuration['main_database']) ? $_configuration['main_database'] : '', + // Only relevant for pdo_sqlite, specifies the path to the SQLite database. + //'path' => isset($_configuration['db_path']) ? $_configuration['db_path'] : '', + // Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8, + //'port' => isset($_configuration['db_port']) ? $_configuration['db_port'] : '', +]; + +try { + $database = new \Database(); + $connection = $database->connect( + $dbParams, + $_configuration['root_sys'], + $_configuration['root_sys'], + true + ); + + $list = $connection->getSchemaManager()->listDatabases(); + echo $plugin->get_lang('connectionok'); +} catch (Exception $e) { + echo $plugin->get_lang('badconnection'); + exit(); +} diff --git a/plugin/vchamilo/views/manage.testdatapath.php b/plugin/vchamilo/views/manage.testdatapath.php new file mode 100644 index 000000000..29e403f2e --- /dev/null +++ b/plugin/vchamilo/views/manage.testdatapath.php @@ -0,0 +1,3 @@ + $settingId]); + + $params = [ + 'title' => $setting['title'], + 'variable' => $setting['variable'], + 'subkey' => $setting['subkey'], + 'category' => $setting['category'], + 'access_url' => $setting['access_url'], + ]; + + foreach ($vchamilos as $chm) { + $table = $chm['main_database'].".settings_current "; + $sql = " SELECT * FROM $table + WHERE + variable = '{{$setting['variable']}}' AND + access_url = '{$setting['access_url']}' + "; + $result = Database::query($sql); + + if (Database::num_rows($result)) { + Database::update($table, ['selected_Value' => $value, ['id' => $settingId]]); + } + } + } + break; + case 'syncthis': + $settingId = isset($_GET['settingid']) ? (int) $_GET['settingid'] : 0; + + if ($settingId) { + $deleteIfEmpty = $_REQUEST['del'] ?? ''; + $value = $_REQUEST['value']; + // Getting the local setting record. + $setting = api_get_settings_params_simple(['id = ?' => $settingId]); + if (empty($setting)) { + return 0; + } + + $params = [ + 'access_url_changeable' => $setting['access_url_changeable'], + 'title' => $setting['title'], + 'variable' => $setting['variable'], + 'subkey' => $setting['subkey'], + 'category' => $setting['category'], + 'type' => $setting['type'], + 'comment' => $setting['comment'], + 'access_url' => $setting['access_url'], + ]; + + $errors = ''; + foreach ($vchamilos as $instance) { + $table = 'settings_current'; + $config = new Configuration(); + $connectionParams = [ + 'dbname' => $instance['main_database'], + 'user' => $instance['db_user'], + 'password' => $instance['db_password'], + 'host' => $instance['db_host'], + 'driver' => 'pdo_mysql', + ]; + try { + $connection = DriverManager::getConnection($connectionParams, $config); + + $variable = $setting['variable']; + $subKey = $setting['subkey']; + $category = $setting['category']; + $accessUrl = $setting['access_url']; + + if ($deleteIfEmpty && empty($value)) { + $connection->delete($table, ['selected_value' => $value, 'variable' => $variable, 'access_url' => $accessUrl]); + $case = 'delete'; + } else { + $sql = "SELECT * FROM $table + WHERE + variable = '$variable' AND + access_url = '$accessUrl' + "; + $result = $connection->fetchAllAssociative($sql); + + if (!empty($result)) { + //$sql = "UPDATE $table SET selected_value = '$value' WHERE id = $settingId"; + $criteria = ['variable' => $variable]; + if (!empty($subKey)) { + $criteria['subkey'] = $subKey; + } + if (!empty($category)) { + $criteria['category'] = $category; + } + $connection->update($table, ['selected_value' => $value], $criteria); + } else { + $connection->insert($table, $params); + } + } + } catch (Exception $e) { + echo $e->getMessage(); + } + } + + return $errors; + } else { + return "Bad ID. Non numeric"; + } + break; +} + +return 0; diff --git a/plugin/vchamilo/views/syncparams.php b/plugin/vchamilo/views/syncparams.php new file mode 100644 index 000000000..eb0ef9768 --- /dev/null +++ b/plugin/vchamilo/views/syncparams.php @@ -0,0 +1,83 @@ + 'table']); +$column = 0; +$row = 0; +$headers = [ + '', + $plugin->get_lang('variable').' ['.$plugin->get_lang('subkey').']', + $plugin->get_lang('category'), + $plugin->get_lang('accessurl'), + $plugin->get_lang('value'), + '', +]; +$attrs = ['center' => 'left']; +$table->addRow($headers, $attrs, 'th'); + +foreach ($settings as $param) { + if ($param['subkey'] == 'vchamilo') { + continue; + } + $check = ''; + $attrs = ['center' => 'left']; + $syncButton = ' + + '; + $data = [ + $check, + isset($param['subkey']) && !empty($param['subkey']) ? $param['variable'].' ['.$param['subkey'].']' : $param['variable'], + $param['category'], + $param['access_url'], + ''. + '
    Master value: '.$param['selected_value'], + $syncButton, + ]; + $row = $table->addRow($data, $attrs, 'td'); + $table->setRowAttributes($row, ['id' => 'row_'.$param['id']], true); +} + +$content = '
    '; +$content .= ''; +$content .= $table->toHtml(); +$content .= '
    '; + +Display::addFlash(Display::return_message($plugin->get_lang('Sync your master settings to all instances.'))); + +$interbreadcrumb[] = ['url' => 'manage.php', 'name' => get_lang('VChamilo')]; +$htmlHeadXtra[] = ""; + +$tpl = new Template($plugin->get_lang('SyncSettings'), true, true, false, true, false); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/vchamilo/views/upgrade.php b/plugin/vchamilo/views/upgrade.php new file mode 100644 index 000000000..eac7c3121 --- /dev/null +++ b/plugin/vchamilo/views/upgrade.php @@ -0,0 +1,65 @@ + 'manage.php', 'name' => get_lang('VChamilo')]; + +// Security +api_protect_admin_script(); +Virtual::checkSettings(); + +$plugin = VChamiloPlugin::create(); + +$id = isset($_REQUEST['vid']) ? (int) $_REQUEST['vid'] : 0; + +$instance = Virtual::getInstance($id); +$canBeUpgraded = Virtual::canBeUpgraded($instance); + +$form = new FormValidator('upgrade', 'post', api_get_self().'?vid='.$id); +// Database host. +$form->addHeader(get_lang('Upgrade')); + +$form->addText('root_web', $plugin->get_lang('rootweb')); +$form->addText('db_host', $plugin->get_lang('dbhost')); +$form->addText('db_user', $plugin->get_lang('dbuser')); +$form->addText('main_database', [$plugin->get_lang('maindatabase')]); + +$form->setDefaults((array) $instance); +if ($canBeUpgraded) { + $form->addLabel(get_lang('From'), $canBeUpgraded); + $form->addLabel(get_lang('To'), api_get_setting('chamilo_database_version')); + $form->addButtonSave(get_lang('Upgrade')); +} else { + Display::addFlash(Display::return_message(get_lang('NothingToUpgrade'))); +} + +$form->freeze(); +$content = $form->returnForm(); + +if ($form->validate() && $canBeUpgraded) { + $values = $form->getSubmitValues(); + + require_once api_get_path(SYS_CODE_PATH).'install/install.lib.php'; + + $manager = Virtual::getConnectionFromInstance($instance, true); + if ($manager) { + ob_start(); + $result = migrateSwitch($canBeUpgraded, $manager, false); + $data = ob_get_clean(); + if ($result) { + Display::addFlash(Display::return_message(get_lang('Upgraded'))); + } else { + Display::addFlash(Display::return_message(get_lang('Error'))); + } + $content = $data; + } else { + Display::addFlash(Display::return_message(get_lang('Error'))); + } +} + +$tpl = new Template(get_lang('Upgrade'), true, true, false, true, false); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/whispeakauth/Controller/AuthenticationController.php b/plugin/whispeakauth/Controller/AuthenticationController.php new file mode 100644 index 000000000..64af1bf95 --- /dev/null +++ b/plugin/whispeakauth/Controller/AuthenticationController.php @@ -0,0 +1,366 @@ +plugin->toolIsEnabled()) { + throw new \Exception(get_lang('NotAllowed')); + } + + /** @var array $lpQuestionInfo */ + $lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); + + if (ChamiloSession::read(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, false)) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD); + + if (empty($lpQuestionInfo)) { + $message = $this->plugin->get_lang('MaxAttemptsReached') + .'
    '.$this->plugin->get_lang('LoginWithUsernameAndPassword').''; + + Display::addFlash( + Display::return_message($message, 'warning') + ); + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php'); + exit; + } + + /** @var array $lpItemInfo */ + $lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); + /** @var \learnpath $oLp */ + $oLp = ChamiloSession::read('oLP', null); + /** @var \Exercise $objExercise */ + $objExercise = ChamiloSession::read('objExercise', null); + + $isAuthOnLp = !empty($lpItemInfo) && !empty($oLp); + $isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise); + $showFullPage = !$isAuthOnLp && !$isAuthOnQuiz; + + $user = api_get_user_entity( + ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id() + ); + + $showForm = !$user; + + if ($user) { + if (!WhispeakAuthPlugin::getAuthUidValue($user)) { + $message = Display::return_message($this->plugin->get_lang('SpeechAuthNotEnrolled'), 'warning'); + + if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { + echo $message; + } else { + Display::addFlash($message); + } + + header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php'); + + exit; + } + } + + if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { + echo api_get_js('rtc/RecordRTC.js'); + echo api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); + } + + $request = new ApiRequest(); + $response = $request->createAuthenticationSessionToken($user); + + if (empty($response['text'])) { + $varNumber = mt_rand(1, 6); + $response['text'] = $this->plugin->get_lang("AuthentifySampleText$varNumber"); + } + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']); + + if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { + $template = new \Template('', $showFullPage, $showFullPage, false, true, false); + $template->assign('show_form', $showForm); + $template->assign('sample_text', $response['text']); + + echo $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); + exit; + } + + $this->displayPage( + $showFullPage, + [ + 'show_form' => $showForm, + 'sample_text' => $response['text'], + ] + ); + } + + /** + * @throws \Exception + */ + public function ajax() + { + $userId = api_get_user_id(); + $user2fa = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0); + + $result = []; + + if (!empty($user2fa) || !empty($userId)) { + $isAllowed = !empty($_FILES['audio']); + } else { + $isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']); + } + + if (!$isAllowed || !$this->plugin->toolIsEnabled()) { + throw new \Exception(get_lang('NotAllowed')); + } + + if (!empty($user2fa)) { + $user = api_get_user_entity($user2fa); + } elseif (!empty($userId)) { + $user = api_get_user_entity($userId); + } else { + /** @var User|null $user */ + $user = \UserManager::getRepository()->findOneBy(['username' => $_POST['username']]); + } + + if (!$user) { + throw new \Exception(get_lang('NotFound')); + } + + $audioFilePath = $this->uploadAudioFile($user); + + $failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0); + $maxAttempts = $this->plugin->getMaxAttempts(); + + if ($maxAttempts && $failedLogins >= $maxAttempts) { + throw new \Exception($this->plugin->get_lang('MaxAttemptsReached')); + } + + $token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); + + \ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); + + /** @var array $lpItemInfo */ + $lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); + /** @var array $quizQuestionInfo */ + $quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); + + $success = true; + + $request = new ApiRequest(); + + try { + $request->performAuthentication($token, $user, $audioFilePath); + + $message = $this->plugin->get_lang('AuthentifySuccess'); + } catch (\Exception $exception) { + $message = $this->plugin->get_lang('AuthentifyFailed') + .PHP_EOL + .$exception->getMessage(); + + $success = false; + } + + if (!$success) { + if (!empty($lpItemInfo)) { + $this->plugin->addAttemptInLearningPath( + LogEvent::STATUS_FAILED, + $user->getId(), + $lpItemInfo['lp_item'], + $lpItemInfo['lp'] + ); + } + + if (!empty($quizQuestionInfo)) { + $this->plugin->addAttemptInQuiz( + LogEvent::STATUS_FAILED, + $user->getId(), + $quizQuestionInfo['question'], + $quizQuestionInfo['quiz'] + ); + } + + if (empty($lpItemInfo) && empty($quizQuestionInfo)) { + $this->plugin->addAuthenticationAttempt(LogEvent::STATUS_FAILED, $user->getId()); + } + + $authTokenRequest = new ApiRequest(); + $authTokenResponse = $authTokenRequest->createAuthenticationSessionToken($user); + + if (empty($authTokenResponse['text'])) { + $varNumber = mt_rand(1, 6); + $authTokenResponse['text'] = $this->plugin->get_lang("AuthentifySampleText$varNumber"); + } + + $result['text'] = $authTokenResponse['text']; + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $authTokenResponse['token']); + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins); + + if ($maxAttempts && $failedLogins >= $maxAttempts) { + $message .= PHP_EOL + .''.$this->plugin->get_lang('MaxAttemptsReached').'' + .PHP_EOL.PHP_EOL + .'' + .$this->plugin->get_lang('LoginWithUsernameAndPassword') + .''; + + if (!empty($user2fa)) { + Display::addFlash( + Display::return_message($message, 'warning', false) + ); + } + } else { + $message .= PHP_EOL.$this->plugin->get_lang('TryAgain'); + + if ('true' === api_get_setting('allow_lostpassword')) { + $message .= PHP_EOL + .Display::url( + get_lang('LostPassword'), + api_get_path(WEB_CODE_PATH).'auth/lostPassword.php', + ['target' => $lpItemInfo ? '_top' : '_self'] + ); + } + } + } + + $result['resultHtml'] = Display::return_message( + nl2br($message), + $success ? 'success' : 'warning', + false + ); + + if (!$success && $maxAttempts && $failedLogins >= $maxAttempts) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); + + if (!empty($lpItemInfo)) { + $result['resultHtml'] .= ''; + + return $result; + } + + if (!empty($quizQuestionInfo)) { + $url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, true); + + $result['resultHtml'] .= ""; + + return $result; + } + + $result['resultHtml'] .= ''; + + return $result; + } + + if ($success) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); + + if (!empty($lpItemInfo)) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM); + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); + + $this->plugin->addAttemptInLearningPath( + LogEvent::STATUS_SUCCESS, + $user->getId(), + $lpItemInfo['lp_item'], + $lpItemInfo['lp'] + ); + + $result['resultHtml'] .= ''; + + return $result; + } + + if (!empty($quizQuestionInfo)) { + $quizQuestionInfo['passed'] = true; + $url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo); + + $this->plugin->addAttemptInQuiz( + LogEvent::STATUS_SUCCESS, + $user->getId(), + $quizQuestionInfo['question'], + $quizQuestionInfo['quiz'] + ); + + $result['resultHtml'] .= ''; + + return $result; + } + + if (empty($lpItemInfo) && empty($quizQuestionInfo)) { + $this->plugin->addAuthenticationAttempt(LogEvent::STATUS_SUCCESS, $user->getId()); + } + + $loggedUser = [ + 'user_id' => $user->getId(), + 'status' => $user->getStatus(), + 'uidReset' => true, + ]; + + if (empty($user2fa)) { + ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $user->getId()); + } + + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); + ChamiloSession::write('_user', $loggedUser); + Login::init_user($user->getId(), true); + + $result['resultHtml'] .= ''; + + return $result; + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function displayPage($isFullPage, array $variables) + { + global $htmlHeadXtra; + + $htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); + $htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); + + $pageTitle = $this->plugin->get_title(); + + $template = new \Template($pageTitle, $isFullPage, $isFullPage, !$isFullPage); + + foreach ($variables as $key => $value) { + $template->assign($key, $value); + } + + $pageContent = $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); + + $template->assign('header', $pageTitle); + $template->assign('content', $pageContent); + $template->display_one_col_template(); + } +} diff --git a/plugin/whispeakauth/Controller/BaseController.php b/plugin/whispeakauth/Controller/BaseController.php new file mode 100644 index 000000000..bc0cd2781 --- /dev/null +++ b/plugin/whispeakauth/Controller/BaseController.php @@ -0,0 +1,67 @@ +plugin = \WhispeakAuthPlugin::create(); + } + + /** + * @param bool $isFullPage + * + * @return mixed + */ + abstract protected function displayPage($isFullPage, array $variables); + + /** + * @throws \Exception + * + * @return string + */ + protected function uploadAudioFile(User $user) + { + $pluginName = $this->plugin->get_name(); + + $path = api_upload_file($pluginName, $_FILES['audio'], $user->getId()); + + if (false === $path) { + throw new \Exception(get_lang('UploadError')); + } + + $fullPath = api_get_path(SYS_UPLOAD_PATH).$pluginName.$path['path_to_save']; + $mimeType = mime_content_type($fullPath); + + if ('wav' !== substr($mimeType, -3)) { + $ffmpeg = FFMpeg::create(); + + $audioFile = $ffmpeg->open($fullPath); + + $fullPath = dirname($fullPath).'/audio.wav'; + + $audioFile->save(new Wav(), $fullPath); + } + + return $fullPath; + } +} diff --git a/plugin/whispeakauth/Controller/EnrollmentController.php b/plugin/whispeakauth/Controller/EnrollmentController.php new file mode 100644 index 000000000..6a7bbb4c0 --- /dev/null +++ b/plugin/whispeakauth/Controller/EnrollmentController.php @@ -0,0 +1,116 @@ +plugin->toolIsEnabled()) { + throw new \Exception(get_lang('NotAllowed')); + } + + $user = api_get_user_entity(api_get_user_id()); + + $userIsEnrolled = \WhispeakAuthPlugin::checkUserIsEnrolled($user->getId()); + + if ($userIsEnrolled) { + throw new \Exception($this->plugin->get_lang('SpeechAuthAlreadyEnrolled')); + } + + $request = new ApiRequest(); + $response = $request->createEnrollmentSessionToken($user); + + \ChamiloSession::write(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']); + + $this->displayPage( + true, + [ + 'action' => 'enrollment', + 'sample_text' => $response['text'], + ] + ); + } + + /** + * @throws \Exception + */ + public function ajax() + { + $result = ['resultHtml' => '']; + + if (!$this->plugin->toolIsEnabled() || empty($_FILES['audio'])) { + throw new \Exception(get_lang('NotAllowed')); + } + + $user = api_get_user_entity(api_get_user_id()); + + $audioFilePath = $this->uploadAudioFile($user); + + $token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); + + if (empty($token)) { + throw new \Exception($this->plugin->get_lang('EnrollmentFailed')); + } + + $request = new ApiRequest(); + + try { + $response = $request->createEnrollment($token, $audioFilePath, $user); + } catch (\Exception $exception) { + $enrollTokenRequest = new ApiRequest(); + $enrollTokenResponse = $enrollTokenRequest->createEnrollmentSessionToken($user); + + \ChamiloSession::write(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $enrollTokenResponse['token']); + + return [ + 'resultHtml' => \Display::return_message($exception->getMessage(), 'error'), + 'text' => $enrollTokenResponse['text'], + ]; + } + + \ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT); + + $this->plugin->saveEnrollment($user, $response['speaker']); + + $result['resultHtml'] .= \Display::return_message($this->plugin->get_lang('EnrollmentSuccess'), 'success'); + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function displayPage($isFullPage, array $variables) + { + global $htmlHeadXtra; + + $htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); + $htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); + + $pageTitle = $this->plugin->get_lang('EnrollmentTitle'); + + $template = new \Template($pageTitle); + + foreach ($variables as $key => $value) { + $template->assign($key, $value); + } + + $pageContent = $template->fetch('whispeakauth/view/record_audio.html.twig'); + + $template->assign('header', $pageTitle); + $template->assign('content', $pageContent); + $template->display_one_col_template(); + } +} diff --git a/plugin/whispeakauth/Entity/LogEvent.php b/plugin/whispeakauth/Entity/LogEvent.php new file mode 100644 index 000000000..bec31ede2 --- /dev/null +++ b/plugin/whispeakauth/Entity/LogEvent.php @@ -0,0 +1,145 @@ +id; + } + + /** + * @param int $id + * + * @return LogEvent + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return DateTime + */ + public function getDatetime() + { + return $this->datetime; + } + + /** + * @param DateTime $datetime + * + * @return LogEvent + */ + public function setDatetime($datetime) + { + $this->datetime = $datetime; + + return $this; + } + + /** + * @return int + */ + public function getActionStatus() + { + return $this->actionStatus; + } + + /** + * @param int $actionStatus + * + * @return LogEvent + */ + public function setActionStatus($actionStatus) + { + $this->actionStatus = $actionStatus; + + return $this; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @param User $user + * + * @return LogEvent + */ + public function setUser($user) + { + $this->user = $user; + + return $this; + } + + /** + * @return string + */ + public function getTypeString() + { + return '-'; + } +} diff --git a/plugin/whispeakauth/Entity/LogEventLp.php b/plugin/whispeakauth/Entity/LogEventLp.php new file mode 100644 index 000000000..6b42cf927 --- /dev/null +++ b/plugin/whispeakauth/Entity/LogEventLp.php @@ -0,0 +1,84 @@ +lpItem; + } + + /** + * @param CLpItem $lpItem + * + * @return LogEventLp + */ + public function setLpItem($lpItem) + { + $this->lpItem = $lpItem; + + return $this; + } + + /** + * @return CLp + */ + public function getLp() + { + return $this->lp; + } + + /** + * @param CLp $lp + * + * @return LogEventLp + */ + public function setLp($lp) + { + $this->lp = $lp; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getTypeString() + { + $lpName = $this->lp->getName(); + $itemTitle = $this->getLpItem()->getTitle(); + + return "$lpName > $itemTitle"; + } +} diff --git a/plugin/whispeakauth/Entity/LogEventQuiz.php b/plugin/whispeakauth/Entity/LogEventQuiz.php new file mode 100644 index 000000000..c35c24f39 --- /dev/null +++ b/plugin/whispeakauth/Entity/LogEventQuiz.php @@ -0,0 +1,84 @@ +question; + } + + /** + * @param CQuizQuestion $question + * + * @return LogEventQuiz + */ + public function setQuestion($question) + { + $this->question = $question; + + return $this; + } + + /** + * @return CQuiz + */ + public function getQuiz() + { + return $this->quiz; + } + + /** + * @param CQuiz $quiz + * + * @return LogEventQuiz + */ + public function setQuiz($quiz) + { + $this->quiz = $quiz; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getTypeString() + { + $quiz = strip_tags($this->getQuiz()->getTitle()); + $question = strip_tags($this->getQuestion()->getQuestion()); + + return "$quiz > $question"; + } +} diff --git a/plugin/whispeakauth/README.md b/plugin/whispeakauth/README.md new file mode 100644 index 000000000..b963a2bf9 --- /dev/null +++ b/plugin/whispeakauth/README.md @@ -0,0 +1,23 @@ +Speech authentication with Whispeak +=================================== + +**Notice:** + +This plugin requires the user to grant permission to use the microphone connected on the web browser. Currently, +browsers are limiting this permission to be used only in a secure environment with HTTPS. +**If your portal does not work with HTTPS, then Whispeak authentication may not work.** + +Installation: +------------- + +*Prior to installing/uninstalling this plugin, you will need to make sure the src/Chamilo/PluginBundle/Entity folder is +temporarily writeable by the web server. This might imply a manual change on your server (outside of the Chamilo +interface).* + +1. Install plugin in Chamilo. +2. Set the plugin configuration enabling the plugin and (optionally) set the max attempts. +3. Set the `login_bottom` region to the plugin. +4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file. +5. Optionally, you can add the `menu_administrator` region to se the user logged activities from Whispeak. + +To have more information about whispeak or create an account to be able to use it on Chamilo you can go here Whispeak diff --git a/plugin/whispeakauth/Request/ApiRequest.php b/plugin/whispeakauth/Request/ApiRequest.php new file mode 100644 index 000000000..9a08b70ed --- /dev/null +++ b/plugin/whispeakauth/Request/ApiRequest.php @@ -0,0 +1,230 @@ +plugin = \WhispeakAuthPlugin::create(); + $this->apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); + } + + /** + * Create a session token to perform an enrollment. + * + * @throws \Exception + * + * @return array + */ + public function createEnrollmentSessionToken(User $user) + { + $apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); + $langIso = api_get_language_isocode($user->getLanguage()); + + return $this->sendRequest( + 'get', + 'enroll', + $apiKey, + $langIso + ); + } + + /** + * @param string $token + * @param string $audioFilePath + * + * @throws \Exception + * + * @return array + */ + public function createEnrollment($token, $audioFilePath, User $user) + { + $langIso = api_get_language_isocode($user->getLanguage()); + + return $this->sendRequest( + 'post', + 'enroll', + $token, + $langIso, + [ + [ + 'name' => 'file', + 'contents' => fopen($audioFilePath, 'r'), + 'filename' => basename($audioFilePath), + ], + ] + ); + } + + /** + * @throws \Exception + * + * @return array + */ + public function createAuthenticationSessionToken(User $user = null) + { + $apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); + + $langIso = api_get_language_isocode($user ? $user->getLanguage() : null); + + return $this->sendRequest( + 'get', + 'auth', + $apiKey, + $langIso + ); + } + + /** + * @throws \Exception + * + * @return array + */ + public function deleteEnrollment(User $user) + { + $apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN); + $langIso = api_get_language_isocode($user->getLanguage()); + $userAuthKey = \WhispeakAuthPlugin::getAuthUidValue($user->getId()); + + if (empty($userAuthKey) || empty($userAuthKey->getValue())) { + throw new \Exception(get_plugin_lang('NoEnrollment', 'WhispeakAuthPlugin')); + } + + $queryData = ['speaker' => $userAuthKey->getValue()]; + + return $this->sendRequest( + 'delete', + 'enroll', + $apiKey, + $langIso, + [], + $queryData + ); + } + + /** + * @param string $token + * @param string $audioFilePath + * + * @throws \Exception + * + * @return bool + */ + public function performAuthentication($token, User $user, $audioFilePath) + { + $wsid = \WhispeakAuthPlugin::getAuthUidValue($user->getId()); + + if (empty($wsid)) { + throw new \Exception($this->plugin->get_lang('SpeechAuthNotEnrolled')); + } + + $langIso = api_get_language_isocode($user ? $user->getLanguage() : null); + + $this->sendRequest( + 'post', + 'auth', + $token, + $langIso, + [ + [ + 'name' => 'speaker', + 'contents' => $wsid->getValue(), + ], + [ + 'name' => 'file', + 'contents' => fopen($audioFilePath, 'r'), + 'filename' => basename($audioFilePath), + ], + ] + ); + } + + /** + * @param string $method + * @param string $uri + * @param string $authBearer + * @param string $lang + * @param array $queryParams + * + * @throws \GuzzleHttp\Exception\GuzzleException + * + * @return array + */ + private function sendRequest($method, $uri, $authBearer, $lang, array $multipart = [], $queryParams = []) + { + $httpClient = new Client(['base_uri' => $this->plugin->getApiUrl()]); + + $options = []; + $options['headers'] = [ + 'Authorization' => "Bearer $authBearer", + 'Accept-Language' => $lang, + ]; + + if ($queryParams) { + $options['query'] = $queryParams; + } else { + $options['multipart'] = $multipart; + } + + try { + $responseBody = $httpClient + ->request( + $method, + $uri, + $options + ) + ->getBody() + ->getContents(); + + return json_decode($responseBody, true); + } catch (RequestException $requestException) { + if (!$requestException->hasResponse()) { + throw new \Exception($requestException->getMessage()); + } + + $responseBody = $requestException->getResponse()->getBody()->getContents(); + $json = json_decode($responseBody, true); + + $message = ''; + + if (isset($json['asserts'])) { + foreach ($json['asserts'] as $assert) { + if ('invalid_' === substr($assert['value'], 0, 8)) { + $message .= $assert['message'].PHP_EOL; + } + } + } elseif (empty($json['message'])) { + $message = $requestException->getMessage(); + } else { + $message = is_array($json['message']) ? implode(PHP_EOL, $json['message']) : $json['message']; + } + + throw new \Exception($message); + } catch (Exception $exception) { + throw new \Exception($exception->getMessage()); + } + } +} diff --git a/plugin/whispeakauth/WhispeakAuthPlugin.php b/plugin/whispeakauth/WhispeakAuthPlugin.php new file mode 100644 index 000000000..d1562beed --- /dev/null +++ b/plugin/whispeakauth/WhispeakAuthPlugin.php @@ -0,0 +1,804 @@ + 'boolean', + self::SETTING_API_URL => 'text', + self::SETTING_TOKEN => 'text', + self::SETTING_MAX_ATTEMPTS => 'text', + self::SETTING_2FA => 'boolean', + ] + ); + } + + /** + * Get the admin URL for the plugin if Plugin::isAdminPlugin is true. + * + * @return string + */ + public function getAdminUrl() + { + $webPath = api_get_path(WEB_PLUGIN_PATH).$this->get_name(); + + return "$webPath/admin.php"; + } + + /** + * @return WhispeakAuthPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * @throws \Doctrine\ORM\Tools\ToolsException + */ + public function install() + { + $this->installExtraFields(); + $this->installEntities(); + $this->installHook(); + } + + public function uninstall() + { + $this->uninstallHook(); + $this->uninstallExtraFields(); + $this->uninstallEntities(); + } + + /** + * @return ExtraField + */ + public static function getAuthUidExtraField() + { + $em = Database::getManager(); + $efRepo = $em->getRepository('ChamiloCoreBundle:ExtraField'); + + /** @var ExtraField $extraField */ + $extraField = $efRepo->findOneBy( + [ + 'variable' => self::EXTRAFIELD_AUTH_UID, + 'extraFieldType' => ExtraField::USER_FIELD_TYPE, + ] + ); + + return $extraField; + } + + /** + * @return ExtraField + */ + public static function getLpItemExtraField() + { + $efRepo = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraField'); + + /** @var ExtraField $extraField */ + $extraField = $efRepo->findOneBy( + [ + 'variable' => self::EXTRAFIELD_LP_ITEM, + 'extraFieldType' => ExtraField::LP_ITEM_FIELD_TYPE, + ] + ); + + return $extraField; + } + + /** + * @return ExtraField + */ + public static function getQuizQuestionExtraField() + { + $efRepo = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraField'); + + /** @var ExtraField $extraField */ + $extraField = $efRepo->findOneBy( + [ + 'variable' => self::EXTRAFIELD_QUIZ_QUESTION, + 'extraFieldType' => ExtraField::QUESTION_FIELD_TYPE, + ] + ); + + return $extraField; + } + + /** + * @param int $userId + * + * @return ExtraFieldValues + */ + public static function getAuthUidValue($userId) + { + $extraField = self::getAuthUidExtraField(); + $em = Database::getManager(); + $efvRepo = $em->getRepository('ChamiloCoreBundle:ExtraFieldValues'); + + /** @var ExtraFieldValues $value */ + $value = $efvRepo->findOneBy(['field' => $extraField, 'itemId' => $userId]); + + return $value; + } + + /** + * Get the whispeak_lp_item value for a LP item ID. + * + * @param int $lpItemId + * + * @return array|false + */ + public static function getLpItemValue($lpItemId) + { + $efv = new ExtraFieldValue('lp_item'); + $value = $efv->get_values_by_handler_and_field_variable($lpItemId, self::EXTRAFIELD_LP_ITEM); + + return $value; + } + + /** + * @param int $lpItemId + * + * @return bool + */ + public static function isLpItemMarked($lpItemId) + { + if (!self::create()->isEnabled()) { + return false; + } + + $value = self::getLpItemValue($lpItemId); + + return !empty($value) && !empty($value['value']); + } + + /** + * Get the whispeak_quiz_question value for a quiz question ID. + * + * @param int $questionId + * + * @return array|false + */ + public static function getQuizQuestionValue($questionId) + { + $efv = new ExtraFieldValue('question'); + $value = $efv->get_values_by_handler_and_field_variable($questionId, self::EXTRAFIELD_QUIZ_QUESTION); + + return $value; + } + + /** + * @param int $questionId + * + * @return bool + */ + public static function isQuizQuestionMarked($questionId) + { + if (!self::create()->isEnabled()) { + return false; + } + + $value = self::getQuizQuestionValue($questionId); + + return !empty($value) && !empty($value['value']); + } + + /** + * @param int $questionId + * + * @return bool + */ + public static function questionRequireAuthentify($questionId) + { + $isMarked = self::isQuizQuestionMarked($questionId); + + if (!$isMarked) { + return false; + } + + $questionInfo = ChamiloSession::read(self::SESSION_QUIZ_QUESTION, []); + + if (empty($questionInfo)) { + return true; + } + + if ((int) $questionId !== $questionInfo['question']) { + return true; + } + + if (false === $questionInfo['passed']) { + return true; + } + + return false; + } + + /** + * @param int $userId + * + * @return bool + */ + public static function checkUserIsEnrolled($userId) + { + $value = self::getAuthUidValue($userId); + + if (empty($value)) { + return false; + } + + return !empty($value->getValue()); + } + + /** + * @return string + */ + public static function getEnrollmentUrl() + { + return api_get_path(WEB_PLUGIN_PATH).'whispeakauth/enrollment.php'; + } + + /** + * @param string $uid + * + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function saveEnrollment(User $user, $uid) + { + $em = Database::getManager(); + $extraFieldValue = self::getAuthUidValue($user->getId()); + + if (empty($extraFieldValue)) { + $extraField = self::getAuthUidExtraField(); + $now = new DateTime('now', new DateTimeZone('UTC')); + + $extraFieldValue = new ExtraFieldValues(); + $extraFieldValue + ->setField($extraField) + ->setItemId($user->getId()) + ->setUpdatedAt($now); + } + + $extraFieldValue->setValue($uid); + + $em->persist($extraFieldValue); + $em->flush(); + } + + /** + * @return bool + */ + public function toolIsEnabled() + { + return 'true' === $this->get(self::SETTING_ENABLE); + } + + /** + * Access not allowed when tool is not enabled. + * + * @param bool $printHeaders Optional. Print headers. + */ + public function protectTool($printHeaders = true) + { + if ($this->toolIsEnabled()) { + return; + } + + api_not_allowed($printHeaders); + } + + /** + * Get the max_attemtps option. + * + * @return int + */ + public function getMaxAttempts() + { + return (int) $this->get(self::SETTING_MAX_ATTEMPTS); + } + + /** + * Install hook when saving the plugin configuration. + * + * @return WhispeakAuthPlugin + */ + public function performActionsAfterConfigure() + { + $observer = WhispeakConditionalLoginHook::create(); + + if ('true' === $this->get(self::SETTING_2FA)) { + HookConditionalLogin::create()->attach($observer); + } else { + HookConditionalLogin::create()->detach($observer); + } + + return $this; + } + + /** + * This method will call the Hook management insertHook to add Hook observer from this plugin. + */ + public function installHook() + { + $observer = WhispeakMyStudentsLpTrackingHook::create(); + HookMyStudentsLpTracking::create()->attach($observer); + + $observer = WhispeakMyStudentsQuizTrackingHook::create(); + HookMyStudentsQuizTracking::create()->attach($observer); + } + + /** + * This method will call the Hook management deleteHook to disable Hook observer from this plugin. + */ + public function uninstallHook() + { + $observer = WhispeakConditionalLoginHook::create(); + HookConditionalLogin::create()->detach($observer); + + $observer = WhispeakMyStudentsLpTrackingHook::create(); + HookMyStudentsLpTracking::create()->detach($observer); + } + + /** + * @param int $userId + * + * @throws \Doctrine\ORM\OptimisticLockException + * + * @return bool + */ + public static function deleteEnrollment($userId) + { + $extraFieldValue = self::getAuthUidValue($userId); + + if (empty($extraFieldValue)) { + return false; + } + + $em = Database::getManager(); + $em->remove($extraFieldValue); + $em->flush(); + + return true; + } + + /** + * Check if the WhispeakAuth plugin is installed and enabled. + * + * @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled + */ + public function isEnabled(bool $checkEnabled = false): bool + { + return parent::isEnabled() && 'true' === api_get_plugin_setting('whispeakauth', self::SETTING_ENABLE); + } + + /** + * @param int $lpItemId + * + * @return bool + */ + public static function isAllowedToSaveLpItem($lpItemId) + { + if (!self::isLpItemMarked($lpItemId)) { + return true; + } + + $markedItem = ChamiloSession::read(self::SESSION_LP_ITEM, []); + + if (empty($markedItem)) { + return true; + } + + if ((int) $lpItemId !== (int) $markedItem['lp_item']) { + return true; + } + + return false; + } + + /** + * Display a error message. + * + * @param string|null $error Optional. The message text + */ + public static function displayNotAllowedMessage($error = null) + { + $error = empty($error) ? get_lang('NotAllowed') : $error; + + echo Display::return_message($error, 'error', false); + + exit; + } + + /** + * @param int $questionId + * + * @throws Exception + * + * @return string + */ + public static function quizQuestionAuthentify($questionId, Exercise $exercise) + { + ChamiloSession::write( + self::SESSION_QUIZ_QUESTION, + [ + 'quiz' => (int) $exercise->iid, + 'question' => (int) $questionId, + 'url_params' => $_SERVER['QUERY_STRING'], + 'passed' => false, + ] + ); + + $template = new Template('', false, false, false, true, false, false); + $template->assign('question', $questionId); + $template->assign('exercise', $exercise->iid); + $content = $template->fetch('whispeakauth/view/quiz_question.html.twig'); + + echo $content; + } + + /** + * @param int $status + * @param int $userId + * @param int $lpItemId + * @param int $lpId + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + * + * @return LogEventLp|null + */ + public function addAttemptInLearningPath($status, $userId, $lpItemId, $lpId) + { + $em = Database::getManager(); + + $user = api_get_user_entity($userId); + $lpItem = $em->find('ChamiloCourseBundle:CLpItem', $lpItemId); + $lp = $em->find('ChamiloCourseBundle:CLp', $lpId); + + if (empty($lp) || empty($lpItem)) { + return null; + } + + $logEvent = new LogEventLp(); + $logEvent + ->setLpItem($lpItem) + ->setLp($lp) + ->setUser($user) + ->setDatetime( + api_get_utc_datetime(null, false, true) + ) + ->setActionStatus($status); + + $em->persist($logEvent); + $em->flush(); + + return $logEvent; + } + + /** + * @param int $status + * @param int $userId + * @param int $questionId + * @param int $quizId + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + * + * @return LogEventQuiz|null + */ + public function addAttemptInQuiz($status, $userId, $questionId, $quizId) + { + $em = Database::getManager(); + + $user = api_get_user_entity($userId); + $question = $em->find('ChamiloCourseBundle:CQuizQuestion', $questionId); + $quiz = $em->find('ChamiloCourseBundle:CQuiz', $quizId); + + if (empty($quiz) || empty($question)) { + return null; + } + + $logEvent = new LogEventQuiz(); + $logEvent + ->setQuestion($question) + ->setQuiz($quiz) + ->setUser($user) + ->setDatetime( + api_get_utc_datetime(null, false, true) + ) + ->setActionStatus($status); + + $em->persist($logEvent); + $em->flush(); + + return $logEvent; + } + + /** + * @param int $status + * @param int $userId + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + * + * @return LogEvent|null + */ + public function addAuthenticationAttempt($status, $userId) + { + $em = Database::getManager(); + + $user = api_get_user_entity($userId); + + $logEvent = new LogEvent(); + $logEvent + ->setUser($user) + ->setDatetime( + api_get_utc_datetime(null, false, true) + ) + ->setActionStatus($status); + + $em->persist($logEvent); + $em->flush(); + + return $logEvent; + } + + /** + * @param int $lpId + * @param int $userId + * + * @throws \Doctrine\ORM\Query\QueryException + * + * @return string + */ + public static function countAllAttemptsInLearningPath($lpId, $userId) + { + $query = Database::getManager() + ->createQuery( + 'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventLp log + WHERE log.lp = :lp AND log.user = :user' + ) + ->setParameters(['lp' => $lpId, 'user' => $userId]); + + $totalCount = (int) $query->getSingleScalarResult(); + + return $totalCount; + } + + /** + * @param int $lpId + * @param int $userId + * + * @throws \Doctrine\ORM\Query\QueryException + * + * @return string + */ + public static function countSuccessAttemptsInLearningPath($lpId, $userId) + { + $query = Database::getManager() + ->createQuery( + 'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventLp log + WHERE log.lp = :lp AND log.user = :user AND log.actionStatus = :status' + ) + ->setParameters(['lp' => $lpId, 'user' => $userId, 'status' => LogEvent::STATUS_SUCCESS]); + + $totalCount = (int) $query->getSingleScalarResult(); + + return $totalCount; + } + + /** + * @param int $quizId + * @param int $userId + * + * @throws \Doctrine\ORM\Query\QueryException + * + * @return string + */ + public static function countAllAttemptsInQuiz($quizId, $userId) + { + $query = Database::getManager() + ->createQuery( + 'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventQuiz log + WHERE log.quiz = :quiz AND log.user = :user' + ) + ->setParameters(['quiz' => $quizId, 'user' => $userId]); + + $totalCount = (int) $query->getSingleScalarResult(); + + return $totalCount; + } + + /** + * @param int $quizId + * @param int $userId + * + * @throws \Doctrine\ORM\Query\QueryException + * + * @return string + */ + public static function countSuccessAttemptsInQuiz($quizId, $userId) + { + $query = Database::getManager() + ->createQuery( + 'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventQuiz log + WHERE log.quiz = :quiz AND log.user = :user AND log.actionStatus = :status' + ) + ->setParameters(['quiz' => $quizId, 'user' => $userId, 'status' => LogEvent::STATUS_SUCCESS]); + + $totalCount = (int) $query->getSingleScalarResult(); + + return $totalCount; + } + + /** + * @return string + */ + public function getApiUrl() + { + $url = $this->get(self::SETTING_API_URL); + + return trim($url, " \t\n\r \v/").'/'; + } + + /** + * Install extra fields for user, learning path and quiz question. + */ + private function installExtraFields() + { + UserManager::create_extra_field( + self::EXTRAFIELD_AUTH_UID, + \ExtraField::FIELD_TYPE_TEXT, + $this->get_lang('Whispeak uid'), + '' + ); + + LpItem::createExtraField( + self::EXTRAFIELD_LP_ITEM, + \ExtraField::FIELD_TYPE_CHECKBOX, + $this->get_lang('MarkForSpeechAuthentication'), + '0', + true, + true + ); + + $extraField = new \ExtraField('question'); + $params = [ + 'variable' => self::EXTRAFIELD_QUIZ_QUESTION, + 'field_type' => \ExtraField::FIELD_TYPE_CHECKBOX, + 'display_text' => $this->get_lang('MarkForSpeechAuthentication'), + 'default_value' => '0', + 'changeable' => true, + 'visible_to_self' => true, + 'visible_to_others' => false, + ]; + + $extraField->save($params); + } + + /** + * Install the Doctrine's entities. + * + * @throws \Doctrine\ORM\Tools\ToolsException + */ + private function installEntities() + { + $em = Database::getManager(); + + if ($em->getConnection()->getSchemaManager()->tablesExist(['whispeak_log_event'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(LogEvent::class), + $em->getClassMetadata(LogEventLp::class), + $em->getClassMetadata(LogEventQuiz::class), + ] + ); + } + + /** + * Uninstall extra fields for user, learning path and quiz question. + */ + private function uninstallExtraFields() + { + $em = Database::getManager(); + + $authIdExtrafield = self::getAuthUidExtraField(); + + if (!empty($authIdExtrafield)) { + $em + ->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') + ->execute(['field' => $authIdExtrafield]); + + $em->remove($authIdExtrafield); + $em->flush(); + } + + $lpItemExtrafield = self::getLpItemExtraField(); + + if (!empty($lpItemExtrafield)) { + $em + ->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') + ->execute(['field' => $lpItemExtrafield]); + + $em->remove($lpItemExtrafield); + $em->flush(); + } + + $quizQuestionExtrafield = self::getQuizQuestionExtraField(); + + if (!empty($quizQuestionExtrafield)) { + $em + ->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') + ->execute(['field' => $quizQuestionExtrafield]); + + $em->remove($quizQuestionExtrafield); + $em->flush(); + } + } + + /** + * Uninstall the Doctrine's entities. + */ + private function uninstallEntities() + { + $em = Database::getManager(); + + if (!$em->getConnection()->getSchemaManager()->tablesExist(['whispeak_log_event'])) { + return; + } + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(LogEvent::class), + $em->getClassMetadata(LogEventLp::class), + $em->getClassMetadata(LogEventQuiz::class), + ] + ); + } +} diff --git a/plugin/whispeakauth/WhispeakConditionalLoginHook.php b/plugin/whispeakauth/WhispeakConditionalLoginHook.php new file mode 100644 index 000000000..b4611b0bf --- /dev/null +++ b/plugin/whispeakauth/WhispeakConditionalLoginHook.php @@ -0,0 +1,59 @@ + + * [ + * 'conditional_function' => function (array $userInfo) {}, + * 'url' => '', + * ] + * + * conditional_function returns false to redirect to the url and returns true to continue with the classical login. + * + * @return array + */ + public function hookConditionalLogin(HookConditionalLoginEventInterface $hook) + { + return [ + 'conditional_function' => function (array $userInfo) { + $isEnrolled = WhispeakAuthPlugin::checkUserIsEnrolled($userInfo['user_id']); + + if (!$isEnrolled) { + return true; + } + + $user2fa = (int) ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0); + + if ($user2fa === (int) $userInfo['user_id']) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); + + return true; + } + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $userInfo['user_id']); + + return false; + }, + 'url' => api_get_path(WEB_PLUGIN_PATH).$this->getPluginName().'/authentify.php', + ]; + } +} diff --git a/plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php b/plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php new file mode 100644 index 000000000..85e33278c --- /dev/null +++ b/plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php @@ -0,0 +1,67 @@ +isEnabled()) { + return []; + } + + return [ + 'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'), + 'attrs' => ['class' => 'text-center'], + ]; + } + + /** + * @throws \Doctrine\ORM\Query\QueryException + * + * @return array + */ + public function trackingContent(HookMyStudentsLpTrackingEventInterface $hook) + { + if (false === WhispeakAuthPlugin::create()->isEnabled()) { + return []; + } + + $data = $hook->getEventData(); + + $totalCount = WhispeakAuthPlugin::countAllAttemptsInLearningPath($data['lp_id'], $data['student_id']); + + if (0 === $totalCount) { + return [ + 'value' => '-', + 'attrs' => ['class' => 'text-center'], + ]; + } + + $successCount = WhispeakAuthPlugin::countSuccessAttemptsInLearningPath($data['lp_id'], $data['student_id']); + + $attrs = ['class' => 'text-center ']; + $attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success'; + + return [ + 'value' => Display::tag('strong', "$successCount / $totalCount"), + 'attrs' => $attrs, + ]; + } +} diff --git a/plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php b/plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php new file mode 100644 index 000000000..1f35bb329 --- /dev/null +++ b/plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php @@ -0,0 +1,84 @@ + + * [ + * 'value' => 'Users online', + * 'attrs' => ['class' => 'text-center'], + * ] + * . + * + * @return array + */ + public function trackingHeader(HookMyStudentsQuizTrackingEventInterface $hook) + { + if (false === WhispeakAuthPlugin::create()->isEnabled()) { + return []; + } + + return [ + 'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'), + 'attrs' => [ + 'class' => 'text-center', + ], + ]; + } + + /** + * Return an associative array this value and attributes. + * + * [ + * 'value' => '5 connected users ', + * 'attrs' => ['class' => 'text-center text-success'], + * ] + * . + * + * @throws \Doctrine\ORM\Query\QueryException + * + * @return array + */ + public function trackingContent(HookMyStudentsQuizTrackingEventInterface $hook) + { + if (false === WhispeakAuthPlugin::create()->isEnabled()) { + return []; + } + $data = $hook->getEventData(); + + $totalCount = WhispeakAuthPlugin::countAllAttemptsInQuiz($data['quiz_id'], $data['student_id']); + + if (0 === $totalCount) { + return [ + 'value' => '-', + 'attrs' => ['class' => 'text-center'], + ]; + } + + $successCount = WhispeakAuthPlugin::countSuccessAttemptsInQuiz($data['quiz_id'], $data['student_id']); + + $attrs = ['class' => 'text-center ']; + $attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success'; + + return [ + 'value' => Display::tag('strong', "$successCount / $totalCount"), + 'attrs' => $attrs, + ]; + } +} diff --git a/plugin/whispeakauth/admin.php b/plugin/whispeakauth/admin.php new file mode 100644 index 000000000..c35c21c98 --- /dev/null +++ b/plugin/whispeakauth/admin.php @@ -0,0 +1,149 @@ +protectTool(); + +$form = new FormValidator('frm_filter', 'GET'); +$form->addHeader($plugin->get_lang('ActionRegistryPerUser')); +$slctUsers = $form->addSelectAjax( + 'users', + get_lang('Users'), + [], + [ + 'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like', + 'id' => 'user_id', + 'multiple' => true, + ] +); +$form->addDatePicker('date', get_lang('Date')); +$form->addButtonSearch(get_lang('Search')); +$form->addRule('users', get_lang('ThisFieldIsRequired'), 'required'); +$form->addRule('date', get_lang('ThisFieldIsRequired'), 'required'); + +$results = []; + +if ($form->validate()) { + $formValues = $form->exportValues(); + $userIds = $formValues['users'] ?: []; + /** @var \DateTime $date */ + $starDate = api_get_utc_datetime($formValues['date'], true, true); + $endDate = clone $starDate; + $endDate->modify('next day'); + + $em = Database::getManager(); + $repo = $em->getRepository('ChamiloPluginBundle:WhispeakAuth\LogEvent'); + + foreach ($userIds as $userId) { + $qb = $em->createQueryBuilder(); + $results[$userId] = $qb + ->select('event') + ->from('ChamiloPluginBundle:WhispeakAuth\LogEvent', 'event') + ->where( + $qb->expr()->gte('event.datetime', ':start_date') + ) + ->andWhere( + $qb->expr()->lt('event.datetime', ':end_date') + ) + ->andWhere( + $qb->expr()->eq('event.user', ':user') + ) + ->setParameters( + [ + 'start_date' => $starDate->format('Y-m-d H:i:s'), + 'end_date' => $endDate->format('Y-m-d H:i:s'), + 'user' => $userId, + ] + ) + ->getQuery() + ->getResult(); + } +} + +$pageContent = ''; + +/** + * @var int $userId + * @var array|LogEvent[] $logEvents + */ +foreach ($results as $userId => $logEvents) { + if (empty($logEvents)) { + continue; + } + + $user = $logEvents[0]->getUser(); + + $slctUsers->addOption($user->getCompleteNameWithUsername(), $user->getId()); + + $tableHeaders = [get_lang('DateTime'), get_lang('Type'), get_lang('Item'), get_lang('Result')]; + $tableData = []; + + foreach ($logEvents as $i => $logEvent) { + $type = ''; + + switch (get_class($logEvent)) { + case LogEventQuiz::class: + $type = get_lang('Question'); + break; + case LogEventLp::class: + $type = get_lang('LearningPath'); + break; + } + + $tableData[] = [ + api_convert_and_format_date($logEvent->getDatetime(), DATE_TIME_FORMAT_SHORT), + $type, + $logEvent->getTypeString(), + $logEvent->getActionStatus() === LogEvent::STATUS_SUCCESS + ? Display::span(get_lang('Success'), ['class' => 'text-success']) + : Display::span(get_lang('Failed'), ['class' => 'text-danger']), + ]; + } + + $table = new HTML_Table(['class' => 'data_table table table-bordered table-hover table-striped table-condensed']); + $table->setHeaders($tableHeaders); + $table->setData($tableData); + $table->updateColAttributes(0, ['class' => 'text-center']); + $table->updateColAttributes(3, ['class' => 'text-center']); + + $pageContent .= Display::page_subheader($user->getCompleteNameWithUsername(), null, 'h4'); + $pageContent .= $table->toHtml(); +} + +$interbreadcrumb[] = [ + 'name' => get_lang('Administration'), + 'url' => api_get_path(WEB_CODE_PATH).'admin/index.php', +]; + +$actionsLeft = ''; + +if (!empty($results)) { + $actionsLeft = Display::url( + Display::return_icon('back.png', $plugin->get_lang('Back'), [], ICON_SIZE_MEDIUM), + api_get_self() + ); +} + +$actionsRight = Display::url( + Display::return_icon('delete_terms.png', $plugin->get_lang('Revocation'), [], ICON_SIZE_MEDIUM), + 'revocation.php' +); + +$template = new Template($plugin->get_title()); +$template->assign('actions', Display::toolbarAction('whispeak_admin', [$actionsLeft, $actionsRight])); +$template->assign( + 'content', + $form->returnForm().PHP_EOL.$pageContent +); +$template->display_one_col_template(); diff --git a/plugin/whispeakauth/ajax/authentify_password.php b/plugin/whispeakauth/ajax/authentify_password.php new file mode 100644 index 000000000..1e583ea36 --- /dev/null +++ b/plugin/whispeakauth/ajax/authentify_password.php @@ -0,0 +1,140 @@ +protectTool(false); + +$tokenIsValid = Security::check_token(); + +if (!$tokenIsValid) { + WhispeakAuthPlugin::displayNotAllowedMessage(); +} + +$maxAttempts = $plugin->getMaxAttempts(); +$failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0); + +if ($maxAttempts && $failedLogins >= $maxAttempts) { + echo Display::return_message($plugin->get_lang('MaxAttemptsReached'), 'warning'); + + exit; +} + +$user = api_get_user_entity(api_get_user_id()); +$password = isset($_POST['password']) ? $_POST['password'] : null; + +if (empty($password) || empty($user)) { + WhispeakAuthPlugin::displayNotAllowedMessage(); +} + +if (!in_array($user->getAuthSource(), [PLATFORM_AUTH_SOURCE, CAS_AUTH_SOURCE])) { + WhispeakAuthPlugin::displayNotAllowedMessage(); +} + +/** @var array $lpItemInfo */ +$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); +/** @var array $quizQuestionInfo */ +$quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); + +$isValidPassword = UserManager::checkPassword($user->getPassword(), $password, $user->getSalt(), $user->getId()); +$isActive = $user->isActive(); +$isExpired = empty($user->getExpirationDate()) || $user->getExpirationDate() > api_get_utc_datetime(null, false, true); + +$userPass = true; + +if (!$isValidPassword || !$isActive || !$isExpired) { + if (!empty($lpItemInfo)) { + $plugin->addAttemptInLearningPath( + LogEvent::STATUS_FAILED, + $user->getId(), + $lpItemInfo['lp_item'], + $lpItemInfo['lp'] + ); + } elseif (!empty($quizQuestionInfo)) { + $plugin->addAttemptInQuiz( + LogEvent::STATUS_FAILED, + $user->getId(), + $quizQuestionInfo['question'], + $quizQuestionInfo['quiz'] + ); + } + + $userPass = false; + + $message = $plugin->get_lang('AuthentifyFailed'); + + if (!$isActive) { + $message .= PHP_EOL.get_lang('Account inactive'); + } + + if (!$isExpired) { + $message .= PHP_EOL.get_lang('AccountExpired'); + } + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins); + + if ($maxAttempts && $failedLogins >= $maxAttempts) { + $message .= PHP_EOL.''.$plugin->get_lang('MaxAttemptsReached').''; + } else { + $message .= PHP_EOL.$plugin->get_lang('TryAgain'); + } + + echo Display::return_message($message, 'error', false); + + if (!$maxAttempts || + ($maxAttempts && $failedLogins >= $maxAttempts) + ) { + $userPass = true; + } +} elseif ($isValidPassword) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); + + if (!empty($lpItemInfo)) { + $plugin->addAttemptInLearningPath( + LogEvent::STATUS_SUCCESS, + $user->getId(), + $lpItemInfo['lp_item'], + $lpItemInfo['lp'] + ); + } elseif (!empty($quizQuestionInfo)) { + $plugin->addAttemptInQuiz( + LogEvent::STATUS_SUCCESS, + $user->getId(), + $quizQuestionInfo['question'], + $quizQuestionInfo['quiz'] + ); + } + + echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success'); +} + +if ($userPass) { + $url = ''; + + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS); + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER); + + if ($lpItemInfo) { + ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM); + + $url = $lpItemInfo['src']; + } elseif ($quizQuestionInfo) { + $quizQuestionInfo['passed'] = true; + $url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params']; + + ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo); + } + + if (!empty($url)) { + echo ' + + '; + } +} diff --git a/plugin/whispeakauth/ajax/record_audio.php b/plugin/whispeakauth/ajax/record_audio.php new file mode 100644 index 000000000..53d120812 --- /dev/null +++ b/plugin/whispeakauth/ajax/record_audio.php @@ -0,0 +1,48 @@ +ajax()); + } catch (Exception $exception) { + echo json_encode( + [ + 'resultHtml' => Display::return_message($exception->getMessage(), 'error', false), + ] + ); + } + exit; +} + +if ($isAuthentify) { + $controller = new AuthenticationController(); + + header('Content-Type: application/json'); + + try { + echo json_encode($controller->ajax()); + } catch (Exception $exception) { + echo json_encode( + [ + 'resultHtml' => Display::return_message($exception->getMessage(), 'error', false), + ] + ); + } +} diff --git a/plugin/whispeakauth/assets/js/RecordAudio.js b/plugin/whispeakauth/assets/js/RecordAudio.js new file mode 100644 index 000000000..35dfda001 --- /dev/null +++ b/plugin/whispeakauth/assets/js/RecordAudio.js @@ -0,0 +1,191 @@ +/* For licensing terms, see /license.txt */ + +window.RecordAudio = (function () { + var timerInterval = 0, + $txtTimer = null; + + function startTimer() { + stopTimer(); + + $txtTimer = $('#txt-timer'); + + $txtTimer.text('00:00').css('visibility', 'visible'); + + var timerData = { + hour: 0, + minute: 0, + second: 0 + }; + + timerInterval = setInterval(function(){ + timerData.second++; + + if (timerData.second >= 60) { + timerData.second = 0; + timerData.minute++; + } + + + $txtTimer.text( + function () { + var txtSeconds = timerData.minute < 10 ? '0' + timerData.minute : timerData.minute, + txtMinutes = timerData.second < 10 ? '0' + timerData.second : timerData.second; + + return txtSeconds + ':' + txtMinutes; + } + ); + }, 1000); + } + + function stopTimer() { + if (timerInterval) { + clearInterval(timerInterval); + } + + if ($txtTimer) { + $txtTimer.css('visibility', 'hidden'); + } + } + + function useRecordRTC(rtcInfo) { + $(rtcInfo.blockId).show(); + + var mediaConstraints = {audio: true}, + localStream = null, + recordRTC = null, + btnStart = $(rtcInfo.btnStartId), + btnStop = $(rtcInfo.btnStopId), + tagAudio = $(rtcInfo.plyrPreviewId); + + function saveAudio() { + var recordedBlob = recordRTC.getBlob(); + + if (!recordedBlob) { + return; + } + + var btnStopText = btnStop.html(); + var fileExtension = recordedBlob.type.split('/')[1]; + + var formData = new FormData(); + formData.append('audio', recordedBlob, 'audio.' + fileExtension); + + for (var prop in rtcInfo.data) { + if (!rtcInfo.data.hasOwnProperty(prop)) { + continue; + } + + formData.append(prop, rtcInfo.data[prop]); + } + + $.ajax({ + url: _p.web_plugin + 'whispeakauth/ajax/record_audio.php', + data: formData, + processData: false, + contentType: false, + type: 'POST', + beforeSend: function () { + btnStart.prop('disabled', true); + btnStop.prop('disabled', true).text(btnStop.data('loadingtext')); + } + }).done(function (response) { + if (response.text) { + $('#txt-sample-text').text(response.text); + } + + $('#messages-deck').html(response.resultHtml); + + if ($('#messages-deck > .alert.alert-success').length > 0) { + tagAudio.parents('#audio-wrapper').addClass('hidden').removeClass('show'); + } else { + tagAudio.parents('#audio-wrapper').removeClass('hidden').addClass('show'); + } + + btnStop.prop('disabled', true).html(btnStopText).parent().addClass('hidden'); + + if ($('#messages-deck > .alert.alert-success').length > 0 || + $('#messages-deck > .alert.alert-warning [data-reach-attempts]').length > 0 + ) { + btnStart.prop('disabled', true); + } else { + btnStart.prop('disabled', false); + } + + btnStart.parent().removeClass('hidden'); + }); + } + + btnStart.on('click', function () { + tagAudio.prop('src', ''); + + function successCallback(stream) { + localStream = stream; + + recordRTC = RecordRTC(stream, { + recorderType: RecordRTC.StereoAudioRecorder, + type: 'audio', + mimeType: 'audio/wav', + numberOfAudioChannels: 2 + }); + recordRTC.startRecording(); + + btnStop.prop('disabled', false).parent().removeClass('hidden'); + btnStart.prop('disabled', true).parent().addClass('hidden'); + tagAudio.removeClass('show').parents('#audio-wrapper').addClass('hidden'); + + $('.fa-microphone').addClass('text-danger'); + + startTimer(); + } + + function errorCallback(error) { + alert(error); + } + + if(!!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia)) { + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + navigator.getUserMedia(mediaConstraints, successCallback, errorCallback); + return; + } + + navigator.mediaDevices.getUserMedia(mediaConstraints) + .then(successCallback) + .catch(errorCallback); + }); + + btnStop.on('click', function () { + if (!recordRTC) { + return; + } + + $('.fa-microphone').removeClass('text-danger'); + + stopTimer(); + + recordRTC.stopRecording(function (audioURL) { + tagAudio.prop('src', audioURL); + + localStream.getTracks()[0].stop(); + + saveAudio(); + }); + }); + } + + return { + init: function (rtcInfo) { + $(rtcInfo.blockId).hide(); + + var userMediaEnabled = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) || + !!navigator.webkitGetUserMedia || + !!navigator.mozGetUserMedia || + !!navigator.getUserMedia; + + if (!userMediaEnabled) { + return; + } + + useRecordRTC(rtcInfo); + } + }; +})(); \ No newline at end of file diff --git a/plugin/whispeakauth/authentify.php b/plugin/whispeakauth/authentify.php new file mode 100644 index 000000000..114ac052f --- /dev/null +++ b/plugin/whispeakauth/authentify.php @@ -0,0 +1,17 @@ +index(); +} catch (Exception $exception) { + api_not_allowed( + true, + Display::return_message($exception->getMessage(), 'warning') + ); +} diff --git a/plugin/whispeakauth/authentify_password.php b/plugin/whispeakauth/authentify_password.php new file mode 100644 index 000000000..ab450adfe --- /dev/null +++ b/plugin/whispeakauth/authentify_password.php @@ -0,0 +1,79 @@ +protectTool(); + +$userId = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id(); + +/** @var array $lpItemInfo */ +$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []); +/** @var learnpath $oLp */ +$oLp = ChamiloSession::read('oLP', null); +/** @var array $lpQuestionInfo */ +$lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []); +/** @var Exercise $objExercise */ +$objExercise = ChamiloSession::read('objExercise', null); + +$isAuthOnLp = !empty($lpItemInfo) && !empty($oLp); +$isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise); + +$showFullPage = !$isAuthOnLp && !$isAuthOnQuiz; + +if (empty($userId)) { + api_not_allowed($showFullPage); +} + +if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { + echo Display::return_message( + $plugin->get_lang('MaxAttemptsReached').'
    ' + .''.$plugin->get_lang('LoginWithUsernameAndPassword').'', + 'warning', + false + ); +} + +$form = new FormValidator( + 'form-login', + 'POST', + api_get_path(WEB_PLUGIN_PATH).'whispeakauth/ajax/authentify_password.php', + null, + null, + FormValidator::LAYOUT_BOX_NO_LABEL +); +$form->addElement( + 'password', + 'password', + get_lang('Pass'), + ['id' => 'password', 'icon' => 'lock fa-fw', 'placeholder' => get_lang('Pass')] +); +$form->addHidden('sec_token', ''); +$form->setConstants(['sec_token' => Security::get_token()]); +$form->addButton('submitAuth', get_lang('LoginEnter'), 'check', 'primary', 'default', 'btn-block'); + +$template = new Template( + !$showFullPage ? '' : $plugin->get_title(), + $showFullPage, + $showFullPage, + false, + true, + false +); +$template->assign('form', $form->returnForm()); + +$content = $template->fetch('whispeakauth/view/authentify_password.html.twig'); + +if (!empty($lpQuestionInfo) && empty($lpItemInfo)) { + echo $content; + + exit; +} + +$template->assign('header', $plugin->get_title()); +$template->assign('content', $content); +$template->display_one_col_template(); diff --git a/plugin/whispeakauth/enrollment.php b/plugin/whispeakauth/enrollment.php new file mode 100644 index 000000000..1eb4e5f0f --- /dev/null +++ b/plugin/whispeakauth/enrollment.php @@ -0,0 +1,21 @@ +index(); +} catch (Exception $exception) { + api_not_allowed( + true, + Display::return_message($exception->getMessage(), 'warning') + ); +} diff --git a/plugin/whispeakauth/index.php b/plugin/whispeakauth/index.php new file mode 100644 index 000000000..420d242d3 --- /dev/null +++ b/plugin/whispeakauth/index.php @@ -0,0 +1,14 @@ +toolIsEnabled()) { + echo Display::toolbarButton( + $plugin->get_lang('SpeechAuthentication'), + api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify.php', + 'sign-in', + 'info', + ['class' => 'btn-block'] + ); +} diff --git a/plugin/whispeakauth/install.php b/plugin/whispeakauth/install.php new file mode 100644 index 000000000..adf5155af --- /dev/null +++ b/plugin/whispeakauth/install.php @@ -0,0 +1,4 @@ +install(); diff --git a/plugin/whispeakauth/lang/english.php b/plugin/whispeakauth/lang/english.php new file mode 100644 index 000000000..68061492e --- /dev/null +++ b/plugin/whispeakauth/lang/english.php @@ -0,0 +1,48 @@ +$_configuration[\'whispeak_auth_enabled\'] = true; in the configuration.php file'; +$strings['api_url'] = 'API URL'; +$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; +$strings['token'] = 'API key'; +$strings['max_attempts'] = 'Max attempts'; +$strings['max_attempts_help'] = '(Optional) If the Whispeak authentication is failed x times, then ask and verify the password of the user'; +$strings['2fa'] = 'Two-Factor Authentication'; +$strings['2fa_help'] = 'Allows extend the login page with a Two-Factor Authentication process. After the classic login, the user must authenticate through Whispeak.'; +$strings['ActionRegistryPerUser'] = 'Action registry per user'; + +$strings['EnrollmentSampleText'] = 'The famous Mona Lisa painting was painted by Leonardo Da Vinci.'; +$strings['AuthentifySampleText1'] = 'Dropping Like Flies.'; +$strings['AuthentifySampleText2'] = 'Keep Your Eyes Peeled.'; +$strings['AuthentifySampleText3'] = 'The fox screams at midnight.'; +$strings['AuthentifySampleText4'] = 'Go Out On a Limb.'; +$strings['AuthentifySampleText5'] = 'Under the Water.'; +$strings['AuthentifySampleText6'] = 'Barking Up The Wrong Tree.'; +$strings['RepeatThisPhrase'] = 'Allow audio recording and then read this sentence out loud:'; +$strings['SpeechAuthAlreadyEnrolled'] = 'Speech authentication already enrolled previously.'; +$strings['SpeechAuthNotEnrolled'] = 'Speech authentication not enrolled previously.'; +$strings['SpeechAuthentication'] = 'Speech authentication'; +$strings['EnrollmentFailed'] = 'Enrollment failed.'; +$strings['EnrollmentSuccess'] = 'Enrollment success.'; +$strings['AuthentifyFailed'] = 'Login failed.'; +$strings['AuthentifySuccess'] = 'Authentication success!'; +$strings['TryAgain'] = 'Try again'; +$strings['MaxAttemptsReached'] = 'You reached the maximum number of attempts allowed.'; +$strings['LoginWithUsernameAndPassword'] = 'You should login using the username and password.'; +$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'You need to identify yourself to answer this question.'; +$strings['IdentifyMe'] = 'Identify me'; +$strings['PleaseWaitWhileLoading'] = "Please wait while loading..."; +$strings['Quality'] = 'Quality'; +$strings['Failed'] = "Failed"; +$strings['ActivityId'] = "Activity ID"; +$strings['Success'] = "Success"; +$strings['MarkForSpeechAuthentication'] = 'Mark it for speech authentication'; +$strings['EnrollmentTitle'] = "Enrollment to generate voice print with Whispeak"; +$strings['Revocation'] = "Revocation"; +$strings['DeleteEnrollments'] = "Delete enrollments"; +$strings['NoEnrollment'] = "No enrollment."; +$strings['EnrollmentDeleted'] = "Enrollment deleted"; diff --git a/plugin/whispeakauth/lang/french.php b/plugin/whispeakauth/lang/french.php new file mode 100644 index 000000000..990d0a7f1 --- /dev/null +++ b/plugin/whispeakauth/lang/french.php @@ -0,0 +1,48 @@ +Ajoutez $_configuration[\'whispeak_auth_enabled\'] = true; dans le fichier configuration.php

    '; +$strings['api_url'] = 'URL de l\'API'; +$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1.1/'; +$strings['token'] = 'Clef API'; +$strings['max_attempts'] = 'Tentatives maximum'; +$strings['max_attempts_help'] = '(Optionnel) Si l\'authentification Whispeak échoue x fois, alors abandonner et demander le mot de passe de l\'utilisateur'; +$strings['2fa'] = 'Authentification à 2 facteurs (2FA)'; +$strings['2fa_help'] = 'Autoriser l\'extension du formulaire de login par une page d\'authentification forte. Après le login classique, l\'utilisateur/trice devra aussi s\'authentifier au travers de Whispeak.'; +$strings['ActionRegistryPerUser'] = 'Registre d\'actions par utilisateur'; + +$strings['EnrollmentSampleText'] = 'Le fameux chef-d\'oeuvre Mona Lisa a été peint par Léonardo da Vinci.'; +$strings['AuthentifySampleText1'] = 'Tomber comme des mouches.'; +$strings['AuthentifySampleText2'] = 'Restez vigilants.'; +$strings['AuthentifySampleText3'] = 'Le renard hurle à minuit.'; +$strings['AuthentifySampleText4'] = 'Errer dans la campagne.'; +$strings['AuthentifySampleText5'] = 'Sous l\'océan.'; +$strings['AuthentifySampleText6'] = 'Prendre la mouche.'; +$strings['RepeatThisPhrase'] = 'Autorisez l\'enregistrement audio puis lisez cette phrase à voix haute :'; +$strings['SpeechAuthAlreadyEnrolled'] = 'L\'authentification de voix a déjà réussi précédemment.'; +$strings['SpeechAuthNotEnrolled'] = 'L\'authentification de voix n\'a pas encore été enregistrée.'; +$strings['SpeechAuthentication'] = 'Authentification par la voix'; +$strings['EnrollmentFailed'] = 'Échec à l\'inscription.'; +$strings['EnrollmentSuccess'] = 'Inscription réussie.'; +$strings['AuthentifyFailed'] = 'Échec de l\'authentification.'; +$strings['AuthentifySuccess'] = 'Authentification réussie!'; +$strings['TryAgain'] = 'Essayez encore'; +$strings['MaxAttemptsReached'] = 'Vous avez atteint le nombre maximum de tentatives autorisées.'; +$strings['LoginWithUsernameAndPassword'] = 'Authentifiez-vous en utilisant votre mot de passe.'; +$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'Vous devez vous authentifier pour répondre à cette question.'; +$strings['IdentifyMe'] = 'M\'identifier'; +$strings['PleaseWaitWhileLoading'] = 'Connexion au serveur d\'authentification. Veuillez patienter...'; +$strings['Quality'] = 'Quality'; +$strings['Failed'] = "Failed"; +$strings['ActivityId'] = "Activity ID"; +$strings['Success'] = "Success"; +$strings['MarkForSpeechAuthentication'] = 'Cocher pour l\'authentification par la voix'; +$strings['EnrollmentTitle'] = "Enrôlement pour générer l'empreinte vocale avec Whispeak"; +$strings['Revocation'] = "Révocation"; +$strings['DeleteEnrollments'] = "Supprimer les inscriptions"; +$strings['NoEnrollment'] = "Aucune inscription"; +$strings['EnrollmentDeleted'] = "Inscription supprimée."; diff --git a/plugin/whispeakauth/lang/spanish.php b/plugin/whispeakauth/lang/spanish.php new file mode 100644 index 000000000..1b5a389ae --- /dev/null +++ b/plugin/whispeakauth/lang/spanish.php @@ -0,0 +1,48 @@ +Agrega $_configuration[\'whispeak_auth_enabled\'] = true; al archivo configuration.php

    '; +$strings['api_url'] = 'URL del API'; +$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; +$strings['token'] = 'Llave del API'; +$strings['max_attempts'] = 'Máximo de intentos'; +$strings['max_attempts_help'] = '(Opcional) Si la autenticación de Whispeak falla x intentos, preguntar y verificar la contraseña del usuario'; +$strings['2fa'] = 'Autenticación en dos factores'; +$strings['2fa_help'] = 'Permite extender la página de inicio de sesión con un proceso de dos factores. Después del inicio de sesión clásico, el usuario deberá autenticarse a través de Whispeak.'; +$strings['ActionRegistryPerUser'] = 'Registro de acciones por usuario'; + +$strings['EnrollmentSampleText'] = 'El famoso cuadro de Mona Lisa fue pintado por Leonardo Da Vinci.'; +$strings['AuthentifySampleText1'] = 'Cayendo como moscas.'; +$strings['AuthentifySampleText2'] = 'Mantén tus ojos abiertos.'; +$strings['AuthentifySampleText3'] = 'El zorro grita a medianoche.'; +$strings['AuthentifySampleText4'] = 'Ir por las ramas.'; +$strings['AuthentifySampleText5'] = 'Debajo del agua.'; +$strings['AuthentifySampleText6'] = 'Ladrando al árbol equivocado.'; +$strings['RepeatThisPhrase'] = 'Permita la grabación de audio y luego lea esta oración en voz alta:'; +$strings['SpeechAuthAlreadyEnrolled'] = 'Autenticación de voz registrada anteriormente.'; +$strings['SpeechAuthNotEnrolled'] = 'Autenticación de voz no registrada previamente.'; +$strings['SpeechAuthentication'] = 'Atenticación con voz'; +$strings['EnrollmentFailed'] = 'Inscripción fallida.'; +$strings['EnrollmentSuccess'] = 'Inscripción correcta.'; +$strings['AuthentifyFailed'] = 'Inicio de sesión fallido.'; +$strings['AuthentifySuccess'] = '¡Autenticación correcta!'; +$strings['TryAgain'] = 'Intente de nuevo.'; +$strings['MaxAttemptsReached'] = 'Ha alcanzado el número máximo de intentos permitidos.'; +$strings['LoginWithUsernameAndPassword'] = 'Debe iniciar sesión usando su nombre de usuario y contraseña.'; +$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'Necesita identificarse para responder esta pregunta.'; +$strings['IdentifyMe'] = 'Identificarme'; +$strings['PleaseWaitWhileLoading'] = "Por favor, espere mientras dure la carga..."; +$strings['Quality'] = 'Calidad'; +$strings['Failed'] = "Fallido"; +$strings['ActivityId'] = "Identificador de actividad"; +$strings['Success'] = "Satisfactotio"; +$strings['MarkForSpeechAuthentication'] = 'Marcarlo para autenticación con voz'; +$strings['EnrollmentTitle'] = "Inscripción para generar huella de voz con Whispeak"; +$strings['Revocation'] = "Revocación"; +$strings['DeleteEnrollments'] = "Eliminar inscripciones"; +$strings['NoEnrollment'] = "Sin inscripción."; +$strings['EnrollmentDeleted'] = "Inscripción anulada."; diff --git a/plugin/whispeakauth/plugin.php b/plugin/whispeakauth/plugin.php new file mode 100644 index 000000000..c990a9158 --- /dev/null +++ b/plugin/whispeakauth/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/whispeakauth/revocation.php b/plugin/whispeakauth/revocation.php new file mode 100644 index 000000000..675ced4bd --- /dev/null +++ b/plugin/whispeakauth/revocation.php @@ -0,0 +1,94 @@ +protectTool(); + +$pageContent = ''; + +$form = new FormValidator('frm_revocation', 'GET'); +$form->setAttribute('onsubmit', "return confirm('".addslashes(get_lang('AreYouSureToDelete'))."');"); +$slctUsers = $form->addSelectAjax( + 'users', + get_lang('Users'), + [], + [ + 'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like', + 'id' => 'user_id', + 'multiple' => true, + ] +); +$form->addButton('asubmit', $plugin->get_lang('DeleteEnrollments'), 'times', 'danger'); +$form->addRule('users', get_lang('ThisFieldIsRequired'), 'required'); + +$userIds = []; + +if ($form->validate()) { + $formValues = $form->exportValues(); + $userIds = $formValues['users'] ?: []; + + /** @var int $userId */ + foreach ($userIds as $userId) { + $user = api_get_user_entity($userId); + + if (null === $user) { + continue; + } + + $slctUsers->addOption($user->getCompleteNameWithUsername(), $user->getId()); + + $request = new ApiRequest(); + + $pageContent .= Display::page_subheader($user->getCompleteNameWithUsername(), null, 'h4'); + + try { + $request->deleteEnrollment($user); + + $response = WhispeakAuthPlugin::deleteEnrollment($user->getId()); + + $pageContent .= Display::return_message( + $plugin->get_lang('EnrollmentDeleted'), + 'success' + ); + } catch (Exception $e) { + $pageContent .= Display::return_message( + $e->getMessage(), + 'error' + ); + } + } +} + +$interbreadcrumb[] = [ + 'name' => get_lang('Administration'), + 'url' => api_get_path(WEB_CODE_PATH).'admin/index.php', +]; +$interbreadcrumb[] = [ + 'name' => $plugin->get_title(), + 'url' => 'admin.php', +]; + +$actionsLeft = Display::url( + Display::return_icon('back.png', $plugin->get_lang('Back'), [], ICON_SIZE_MEDIUM), + 'admin.php' +); + +$pageTitle = $plugin->get_lang('Revocation'); + +$template = new Template($pageTitle); +$template->assign('actions', Display::toolbarAction('whispeak_admin', [$actionsLeft])); +$template->assign('header', $pageTitle); +$template->assign( + 'content', + $form->returnForm().PHP_EOL.$pageContent +); +$template->display_one_col_template(); diff --git a/plugin/whispeakauth/uninstall.php b/plugin/whispeakauth/uninstall.php new file mode 100644 index 000000000..4f811df4d --- /dev/null +++ b/plugin/whispeakauth/uninstall.php @@ -0,0 +1,4 @@ +uninstall(); diff --git a/plugin/whispeakauth/view/authentify_password.html.twig b/plugin/whispeakauth/view/authentify_password.html.twig new file mode 100644 index 000000000..236859a6c --- /dev/null +++ b/plugin/whispeakauth/view/authentify_password.html.twig @@ -0,0 +1,44 @@ +
    +
    + {{ form }} + +
    + +
    +
    +
    + + diff --git a/plugin/whispeakauth/view/authentify_recorder.html.twig b/plugin/whispeakauth/view/authentify_recorder.html.twig new file mode 100644 index 000000000..a47675112 --- /dev/null +++ b/plugin/whispeakauth/view/authentify_recorder.html.twig @@ -0,0 +1,44 @@ +{% extends 'whispeakauth/view/record_audio.html.twig' %} + +{% block intro %} + {% if show_form %} +
    +
    + +
    + +
    +
    +
    + +
    + {% endif %} + + {{ parent() }} +{% endblock %} + +{% block config_data %} + {% if show_form %} + $('#username').on('change', function () { + $('#record-audio-recordrtc, #btn-start-record, #btn-stop-record, #btn-save-record').off('click', ''); + {% endif %} + RecordAudio.init( + { + blockId: '#record-audio-recordrtc', + btnStartId: '#btn-start-record', + btnStopId: '#btn-stop-record', + btnSaveId: '#btn-save-record', + plyrPreviewId: '#record-preview', + data: { + action: 'authentify', + username: $('#username').val() + } + } + ); + + {% if show_form %} + }); + {% endif %} +{% endblock %} diff --git a/plugin/whispeakauth/view/quiz_question.html.twig b/plugin/whispeakauth/view/quiz_question.html.twig new file mode 100644 index 000000000..0cd394a43 --- /dev/null +++ b/plugin/whispeakauth/view/quiz_question.html.twig @@ -0,0 +1,45 @@ +
    +

    {{ 'YouNeedToIdentifyYourselfToAnswerThisQuestion'|get_plugin_lang('WhispeakAuthPlugin') }}

    + + +
    + + diff --git a/plugin/whispeakauth/view/record_audio.html.twig b/plugin/whispeakauth/view/record_audio.html.twig new file mode 100644 index 000000000..42d6cfa6b --- /dev/null +++ b/plugin/whispeakauth/view/record_audio.html.twig @@ -0,0 +1,59 @@ +
    +
    + {% block intro %} +

    {{ 'RepeatThisPhrase'|get_plugin_lang('WhispeakAuthPlugin') }}

    +
    +
    +
    + + +
    +
    +

    {{ sample_text }}

    +
    +
    +
    + {% endblock %} + +
    +

    + +

    + +
    +
    +
    +
    +
    + +
    +
    + + diff --git a/plugin/xapi/.htaccess b/plugin/xapi/.htaccess new file mode 100644 index 000000000..a3851a941 --- /dev/null +++ b/plugin/xapi/.htaccess @@ -0,0 +1 @@ +AcceptPathInfo On \ No newline at end of file diff --git a/plugin/xapi/README.md b/plugin/xapi/README.md new file mode 100644 index 000000000..64a7c88c2 --- /dev/null +++ b/plugin/xapi/README.md @@ -0,0 +1,88 @@ +# Experience API (xAPI) + +Allows you to connect to an external Learning Record Store and use activities with the xAPI standard. + +> You can import and use TinCan packages. +> Import CMI5 packages is to be considered a Beta state and still in development. + +**Configuration** + +Set LRS endpoint, username and password to integrate an external LRS in Chamilo LMS. + +The fields "Learning path item viewed", "Learning path ended", "Quiz question answered" and "Quiz ended" allow enabling +hooks when the user views an item in learning path, completes a learning path, answers a quiz question and ends the exam. + +The statements generated with these hooks are logged in Chamilo database, waiting to be sent to the LRS by a cron job. +The cron job to configure on your server is located in `CHAMILO_PATH/plugin/xapi/cron/send_statements.php`. + +**Use the Statement API from Chamilo LMS** + +You can use xAPI's "Statement API" to save some statements from another service. +You need to create credentials (username/password) to do this. First you need to enable the "menu_administrator" region +in the plugin configuration. You will then be able to create the credentials with the new page "Experience API (xAPI)" +inside de Plugins block in the Administration panel. +The endpoint for the statements API is "https://CHAMILO_DOMAIN/plugin/xapi/lrs.php/"; + +```mysql +CREATE TABLE xapi_attachment (identifier INT AUTO_INCREMENT NOT NULL, statement_id VARCHAR(255) DEFAULT NULL, usageType VARCHAR(255) NOT NULL, contentType VARCHAR(255) NOT NULL, length INT NOT NULL, sha2 VARCHAR(255) NOT NULL, display LONGTEXT NOT NULL COMMENT '(DC2Type:json)', hasDescription TINYINT(1) NOT NULL, description LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', fileUrl VARCHAR(255) DEFAULT NULL, content LONGTEXT DEFAULT NULL, INDEX IDX_7148C9A1849CB65B (statement_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_object (identifier INT AUTO_INCREMENT NOT NULL, group_id INT DEFAULT NULL, actor_id INT DEFAULT NULL, verb_id INT DEFAULT NULL, object_id INT DEFAULT NULL, type VARCHAR(255) DEFAULT NULL, activityId VARCHAR(255) DEFAULT NULL, hasActivityDefinition TINYINT(1) DEFAULT NULL, hasActivityName TINYINT(1) DEFAULT NULL, activityName LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', hasActivityDescription TINYINT(1) DEFAULT NULL, activityDescription LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', activityType VARCHAR(255) DEFAULT NULL, activityMoreInfo VARCHAR(255) DEFAULT NULL, mbox VARCHAR(255) DEFAULT NULL, mboxSha1Sum VARCHAR(255) DEFAULT NULL, openId VARCHAR(255) DEFAULT NULL, accountName VARCHAR(255) DEFAULT NULL, accountHomePage VARCHAR(255) DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, referenced_statement_id VARCHAR(255) DEFAULT NULL, activityExtensions_id INT DEFAULT NULL, parentContext_id INT DEFAULT NULL, groupingContext_id INT DEFAULT NULL, categoryContext_id INT DEFAULT NULL, otherContext_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_E2B68640303C7F1D (activityExtensions_id), INDEX IDX_E2B68640FE54D947 (group_id), UNIQUE INDEX UNIQ_E2B6864010DAF24A (actor_id), UNIQUE INDEX UNIQ_E2B68640C1D03483 (verb_id), UNIQUE INDEX UNIQ_E2B68640232D562B (object_id), INDEX IDX_E2B68640988A4CEC (parentContext_id), INDEX IDX_E2B686404F542860 (groupingContext_id), INDEX IDX_E2B68640AEA1B132 (categoryContext_id), INDEX IDX_E2B68640B73EEAB7 (otherContext_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_result (identifier INT AUTO_INCREMENT NOT NULL, extensions_id INT DEFAULT NULL, hasScore TINYINT(1) NOT NULL, scaled DOUBLE PRECISION DEFAULT NULL, raw DOUBLE PRECISION DEFAULT NULL, min DOUBLE PRECISION DEFAULT NULL, max DOUBLE PRECISION DEFAULT NULL, success TINYINT(1) DEFAULT NULL, completion TINYINT(1) DEFAULT NULL, response VARCHAR(255) DEFAULT NULL, duration VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_5971ECBFD0A19400 (extensions_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_verb (identifier INT AUTO_INCREMENT NOT NULL, id VARCHAR(255) NOT NULL, display LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_extensions (identifier INT AUTO_INCREMENT NOT NULL, extensions LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_context (identifier INT AUTO_INCREMENT NOT NULL, instructor_id INT DEFAULT NULL, team_id INT DEFAULT NULL, extensions_id INT DEFAULT NULL, registration VARCHAR(255) DEFAULT NULL, hasContextActivities TINYINT(1) DEFAULT NULL, revision VARCHAR(255) DEFAULT NULL, platform VARCHAR(255) DEFAULT NULL, language VARCHAR(255) DEFAULT NULL, statement VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_3D7771908C4FC193 (instructor_id), UNIQUE INDEX UNIQ_3D777190296CD8AE (team_id), UNIQUE INDEX UNIQ_3D777190D0A19400 (extensions_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_actor (identifier INT AUTO_INCREMENT NOT NULL, type VARCHAR(255) DEFAULT NULL, mbox VARCHAR(255) DEFAULT NULL, mboxSha1Sum VARCHAR(255) DEFAULT NULL, openId VARCHAR(255) DEFAULT NULL, accountName VARCHAR(255) DEFAULT NULL, accountHomePage VARCHAR(255) DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, members VARCHAR(255) NOT NULL, PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_statement (id VARCHAR(255) NOT NULL, actor_id INT DEFAULT NULL, verb_id INT DEFAULT NULL, object_id INT DEFAULT NULL, result_id INT DEFAULT NULL, authority_id INT DEFAULT NULL, context_id INT DEFAULT NULL, created BIGINT DEFAULT NULL, `stored` BIGINT DEFAULT NULL, hasAttachments TINYINT(1) DEFAULT NULL, UNIQUE INDEX UNIQ_BAF6663B10DAF24A (actor_id), UNIQUE INDEX UNIQ_BAF6663BC1D03483 (verb_id), UNIQUE INDEX UNIQ_BAF6663B232D562B (object_id), UNIQUE INDEX UNIQ_BAF6663B7A7B643 (result_id), UNIQUE INDEX UNIQ_BAF6663B81EC865B (authority_id), UNIQUE INDEX UNIQ_BAF6663B6B00C1CF (context_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +ALTER TABLE xapi_attachment ADD CONSTRAINT FK_7148C9A1849CB65B FOREIGN KEY (statement_id) REFERENCES xapi_statement (id); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640303C7F1D FOREIGN KEY (activityExtensions_id) REFERENCES xapi_extensions (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640FE54D947 FOREIGN KEY (group_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B6864010DAF24A FOREIGN KEY (actor_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640C1D03483 FOREIGN KEY (verb_id) REFERENCES xapi_verb (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640232D562B FOREIGN KEY (object_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640988A4CEC FOREIGN KEY (parentContext_id) REFERENCES xapi_context (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B686404F542860 FOREIGN KEY (groupingContext_id) REFERENCES xapi_context (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640AEA1B132 FOREIGN KEY (categoryContext_id) REFERENCES xapi_context (identifier); +ALTER TABLE xapi_object ADD CONSTRAINT FK_E2B68640B73EEAB7 FOREIGN KEY (otherContext_id) REFERENCES xapi_context (identifier); +ALTER TABLE xapi_result ADD CONSTRAINT FK_5971ECBFD0A19400 FOREIGN KEY (extensions_id) REFERENCES xapi_extensions (identifier); +ALTER TABLE xapi_context ADD CONSTRAINT FK_3D7771908C4FC193 FOREIGN KEY (instructor_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_context ADD CONSTRAINT FK_3D777190296CD8AE FOREIGN KEY (team_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_context ADD CONSTRAINT FK_3D777190D0A19400 FOREIGN KEY (extensions_id) REFERENCES xapi_extensions (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663B10DAF24A FOREIGN KEY (actor_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663BC1D03483 FOREIGN KEY (verb_id) REFERENCES xapi_verb (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663B232D562B FOREIGN KEY (object_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663B7A7B643 FOREIGN KEY (result_id) REFERENCES xapi_result (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663B81EC865B FOREIGN KEY (authority_id) REFERENCES xapi_object (identifier); +ALTER TABLE xapi_statement ADD CONSTRAINT FK_BAF6663B6B00C1CF FOREIGN KEY (context_id) REFERENCES xapi_context (identifier); + +CREATE TABLE xapi_shared_statement (id INT AUTO_INCREMENT NOT NULL, uuid VARCHAR(255) DEFAULT NULL, statement LONGTEXT NOT NULL COMMENT '(DC2Type:array)', sent TINYINT(1) DEFAULT '0' NOT NULL, INDEX idx_uuid (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; + +CREATE TABLE xapi_lrs_auth (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; + +CREATE TABLE xapi_tool_launch (id INT AUTO_INCREMENT NOT NULL, c_id INT NOT NULL, session_id INT DEFAULT NULL, title VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, launch_url VARCHAR(255) NOT NULL, activity_id VARCHAR(255) DEFAULT NULL, activity_type VARCHAR(255) DEFAULT NULL, allow_multiple_attempts TINYINT(1) DEFAULT '1' NOT NULL, lrs_url VARCHAR(255) DEFAULT NULL, lrs_auth_username VARCHAR(255) DEFAULT NULL, lrs_auth_password VARCHAR(255) DEFAULT NULL, INDEX IDX_E18CB58391D79BD3 (c_id), INDEX IDX_E18CB583613FECDF (session_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +ALTER TABLE xapi_tool_launch ADD CONSTRAINT FK_E18CB58391D79BD3 FOREIGN KEY (c_id) REFERENCES course (id); +ALTER TABLE xapi_tool_launch ADD CONSTRAINT FK_E18CB583613FECDF FOREIGN KEY (session_id) REFERENCES session (id); + +CREATE TABLE xapi_cmi5_item (id INT AUTO_INCREMENT NOT NULL, tree_root INT DEFAULT NULL, parent_id INT DEFAULT NULL, identifier VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, title LONGTEXT NOT NULL COMMENT '(DC2Type:json)', description LONGTEXT NOT NULL COMMENT '(DC2Type:json)', url VARCHAR(255) DEFAULT NULL, activity_type VARCHAR(255) DEFAULT NULL, launch_method VARCHAR(255) DEFAULT NULL, move_on VARCHAR(255) DEFAULT NULL, mastery_score DOUBLE PRECISION DEFAULT NULL, launch_parameters VARCHAR(255) DEFAULT NULL, entitlement_key VARCHAR(255) DEFAULT NULL, status VARCHAR(255) DEFAULT NULL, lft INT NOT NULL, lvl INT NOT NULL, rgt INT NOT NULL, tool_id INT DEFAULT NULL, INDEX IDX_7CA116D88F7B22CC (tool_id), INDEX IDX_7CA116D8A977936C (tree_root), INDEX IDX_7CA116D8727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +ALTER TABLE xapi_cmi5_item ADD CONSTRAINT FK_7CA116D8A977936C FOREIGN KEY (tree_root) REFERENCES xapi_cmi5_item (id) ON DELETE CASCADE; +ALTER TABLE xapi_cmi5_item ADD CONSTRAINT FK_7CA116D8727ACA70 FOREIGN KEY (parent_id) REFERENCES xapi_cmi5_item (id) ON DELETE CASCADE; + +CREATE TABLE xapi_activity_state (id INT AUTO_INCREMENT NOT NULL, state_id VARCHAR(255) NOT NULL, activity_id VARCHAR(255) NOT NULL, agent LONGTEXT NOT NULL COMMENT '(DC2Type:json)', document_data LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_activity_profile (id INT AUTO_INCREMENT NOT NULL, profile_id VARCHAR(255) NOT NULL, activity_id VARCHAR(255) NOT NULL, document_data LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +``` + +**From 0.2 (beta) [2021-10-15]** +- With the LRS an internal log is registered based on the actor mbox's email or the actor account's name coming from the statement +To update, execute this queries: + +```sql +CREATE TABLE xapi_internal_log (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, statement_id VARCHAR(255) NOT NULL, verb VARCHAR(255) NOT NULL, object_id VARCHAR(255) NOT NULL, activity_name VARCHAR(255) DEFAULT NULL, activity_description VARCHAR(255) DEFAULT NULL, score_scaled DOUBLE PRECISION DEFAULT NULL, score_raw DOUBLE PRECISION DEFAULT NULL, score_min DOUBLE PRECISION DEFAULT NULL, score_max DOUBLE PRECISION DEFAULT NULL, created_at DATETIME DEFAULT NULL, INDEX IDX_C1C667ACA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +ALTER TABLE xapi_internal_log ADD CONSTRAINT FK_C1C667ACA76ED395 FOREIGN KEY (user_id) REFERENCES user (id); +``` + +**From 0.3 (beta) [2021-11-11]** + +- Fix: Add foreign keys with course/session in tool_launch table and foreign key with user in internal_log table. +```sql +ALTER TABLE xapi_internal_log ADD CONSTRAINT FK_C1C667ACA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE; +ALTER TABLE xapi_tool_launch ADD CONSTRAINT FK_E18CB58391D79BD3 FOREIGN KEY (c_id) REFERENCES course (id) ON DELETE CASCADE; +ALTER TABLE xapi_tool_launch ADD CONSTRAINT FK_E18CB583613FECDF FOREIGN KEY (session_id) REFERENCES session (id) ON DELETE CASCADE; +``` diff --git a/plugin/xapi/admin.php b/plugin/xapi/admin.php new file mode 100644 index 000000000..4affc59fd --- /dev/null +++ b/plugin/xapi/admin.php @@ -0,0 +1,196 @@ +getId()}"; + } + + $form = new FormValidator('frm_xapi_auth', 'post', $action); + $form->addText('username', get_lang('Username'), true); + $form->addText('password', get_lang('Password'), true); + $form->addCheckBox('enabled', get_lang('Enabled'), get_lang('Yes')); + + $form->addButtonSave(get_lang('Save')); + + if (null != $auth) { + $form->setDefaults( + [ + 'username' => $auth->getUsername(), + 'password' => $auth->getPassword(), + 'enabled' => $auth->isEnabled(), + ] + ); + } + + return $form; +} + +switch ($request->query->getAlpha('action')) { + case 'add': + $form = createForm(); + + if ($form->validate()) { + $values = $form->exportValues(); + + $auth = new LrsAuth(); + $auth + ->setUsername($values['username']) + ->setPassword($values['password']) + ->setEnabled(isset($values['enabled'])) + ->setCreatedAt( + api_get_utc_datetime(null, false, true) + ); + + $em->persist($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemAdded'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + } + + $pageActions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl + ); + $pageContent = $form->returnForm(); + break; + case 'edit': + $auth = $em->find(LrsAuth::class, $request->query->getInt('id')); + + if (null == $auth) { + api_not_allowed(true); + } + + $form = createForm($auth); + + if ($form->validate()) { + $values = $form->exportValues(); + + $auth + ->setUsername($values['username']) + ->setPassword($values['password']) + ->setEnabled(isset($values['enabled'])) + ->setCreatedAt( + api_get_utc_datetime(null, false, true) + ); + + $em->persist($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemUpdated'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + } + + $pageActions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl + ); + $pageContent = $form->returnForm(); + break; + case 'delete': + $auth = $em->find(LrsAuth::class, $request->query->getInt('id')); + + if (null == $auth) { + api_not_allowed(true); + } + + $em->remove($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemDeleted'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + case 'list': + default: + $pageActions = Display::url( + Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl.'?action=add' + ); + $pageContent = Display::return_message(get_lang('NoData'), 'warning'); + + $auths = $em->getRepository(LrsAuth::class)->findAll(); + + if (count($auths) > 0) { + $row = 0; + + $table = new HTML_Table(['class' => 'table table-striped table-hover']); + $table->setHeaderContents($row, 0, get_lang('Username')); + $table->setHeaderContents($row, 1, get_lang('Password')); + $table->setHeaderContents($row, 2, get_lang('Enabled')); + $table->setHeaderContents($row, 3, get_lang('CreatedAt')); + $table->setHeaderContents($row, 4, get_lang('Actions')); + + foreach ($auths as $auth) { + $row++; + + $actions = [ + Display::url( + Display::return_icon('edit.png', get_lang('Edit')), + $pageBaseUrl.'?action=edit&id='.$auth->getId() + ), + Display::url( + Display::return_icon('delete.png', get_lang('Edit')), + $pageBaseUrl.'?action=delete&id='.$auth->getId() + ), + ]; + + $table->setCellContents($row, 0, $auth->getUsername()); + $table->setCellContents($row, 1, $auth->getPassword()); + $table->setCellContents($row, 2, $auth->isEnabled() ? get_lang('Yes') : get_lang('No')); + $table->setCellContents($row, 3, api_convert_and_format_date($auth->getCreatedAt())); + $table->setCellContents($row, 4, implode(PHP_EOL, $actions)); + } + + $pageContent = $table->toHtml(); + } + break; +} + +$interbreadcrumb[] = [ + 'name' => get_lang('Administration'), + 'url' => api_get_path(WEB_CODE_PATH).'admin/index.php', +]; + +$view = new Template($plugin->get_title()); +$view->assign('actions', Display::toolbarAction('xapi_actions', [$pageActions])); +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/assets/css/cmi5_launch.css b/plugin/xapi/assets/css/cmi5_launch.css new file mode 100644 index 000000000..eaa82c2da --- /dev/null +++ b/plugin/xapi/assets/css/cmi5_launch.css @@ -0,0 +1,40 @@ +.section-global { + margin: 0; +} + +#pnl-left ul { + list-style: none; + margin: 0; + padding: 0; +} +#pnl-left ul li a { + padding: 5px 0; + display: block; +} +#pnl-left ul ul li { + padding: 0 0 0 15px ; +} + +@media (min-width: 992px) { + #pnl-left { + overflow: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + } + #pnl-right { + position: absolute; + right: 0; + top: 0; + bottom: 0; + } + #ifr-content { + min-width: 100%; + min-height: 100%; + padding: 0; + margin: 0; + position: absolute; + right: 0; + } +} \ No newline at end of file diff --git a/plugin/xapi/assets/js/cmi5_launch.js b/plugin/xapi/assets/js/cmi5_launch.js new file mode 100644 index 000000000..22d2d963e --- /dev/null +++ b/plugin/xapi/assets/js/cmi5_launch.js @@ -0,0 +1,2 @@ +$(function () { +}); \ No newline at end of file diff --git a/plugin/xapi/cmi5/launch.php b/plugin/xapi/cmi5/launch.php new file mode 100644 index 000000000..9c9c65536 --- /dev/null +++ b/plugin/xapi/cmi5/launch.php @@ -0,0 +1,188 @@ +find(Cmi5Item::class, $request->query->getInt('id')); +$toolLaunch = $item->getTool(); + +if ($toolLaunch->getId() !== $request->query->getInt('tool')) { + api_not_allowed( + false, + Display::return_message(get_lang('NotAllwed'), 'error') + ); +} + +$plugin = XApiPlugin::create(); +$user = api_get_user_entity(api_get_user_id()); +$nowDate = api_get_utc_datetime(null, false, true)->format('c'); + +$registration = (string) Uuid::uuid4(); +$actor = new Agent( + InverseFunctionalIdentifier::withAccount( + new Account( + $user->getCompleteName(), + IRL::fromString(api_get_path(WEB_PATH)) + ) + ), + $user->getCompleteName() +); +$verb = new Verb( + IRI::fromString('http://adlnet.gov/expapi/verbs/launched'), + LanguageMap::create($plugin->getLangMap('Launched')) +); +$customActivityId = $plugin->generateIri($item->getId(), 'cmi5_item'); + +$activity = new Activity( + $customActivityId, + new Definition( + LanguageMap::create($item->getTitle()), + LanguageMap::create($item->getDescription()), + IRI::fromString($item->getIdentifier()) + ) +); + +$context = (new Context()) + ->withPlatform( + api_get_setting('Institution').' - '.api_get_setting('siteName') + ) + ->withLanguage(api_get_language_isocode()) + ->withRegistration($registration); + +$statementUuid = Uuid::uuid5( + $plugin->get(XApiPlugin::SETTING_UUID_NAMESPACE), + "cmi5_item/{$item->getId()}" +); + +$statement = new Statement( + StatementId::fromUuid($statementUuid), + $actor, + $verb, + $activity, + null, + null, + api_get_utc_datetime(null, false, true), + null, + $context +); + +$statementClient = XApiPlugin::create()->getXApiStatementClient(); + +//try { +// $statementClient->storeStatement($statement); +//} catch (ConflictException $e) { +// echo Display::return_message($e->getMessage(), 'error'); +// +// exit; +//} catch (XApiException $e) { +// echo Display::return_message($e->getMessage(), 'error'); +// +// exit; +//} + +$viewSessionId = (string) Uuid::uuid4(); + +$state = new State( + $activity, + $actor, + 'LMS.LaunchData', + (string) $registration +); + +$documentDataData = []; +$documentDataData['contentTemplate'] = [ + 'extensions' => [ + 'https://w3id.org/xapi/cmi5/context/extensions/sessionid' => $viewSessionId, + ], +]; +$documentDataData['launchMode'] = 'Normal'; +$documentDataData['launchMethod'] = $item->getLaunchMethod(); + +if ($item->getLaunchParameters()) { + $documentDataData['launchParameteres'] = $item->getLaunchParameters(); +} + +if ($item->getMasteryScore()) { + $documentDataData['masteryScore'] = $item->getMasteryScore(); +} + +if ($item->getEntitlementKey()) { + $documentDataData['entitlementKey'] = [ + 'courseStructure' => $item->getEntitlementKey(), + ]; +} + +$documentData = new DocumentData($documentDataData); + +try { + $plugin + ->getXApiStateClient() + ->createOrReplaceDocument( + new StateDocument($state, $documentData) + ); +} catch (Exception $exception) { + echo Display::return_message($exception->getMessage(), 'error'); + + exit; +} + +$launchUrl = $plugin->generateLaunchUrl( + 'cmi5', + $item->getUrl(), + $customActivityId->getValue(), + $actor, + $registration, + $toolLaunch->getLrsUrl(), + $toolLaunch->getLrsAuthUsername(), + $toolLaunch->getLrsAuthPassword(), + $viewSessionId +); + +if ('OwnWindow' === $item->getLaunchMethod()) { + Display::display_reduced_header(); + + echo '

    '; + echo Display::toolbarButton( + $plugin->get_lang('LaunchNewAttempt'), + $launchUrl, + 'external-link fa-fw', + 'success', + [ + 'target' => '_blank', + ] + ); + echo ''; + + Display::display_reduced_footer(); + + exit; +} + +header("Location: $launchUrl"); diff --git a/plugin/xapi/cmi5/token.php b/plugin/xapi/cmi5/token.php new file mode 100644 index 000000000..be04b7e18 --- /dev/null +++ b/plugin/xapi/cmi5/token.php @@ -0,0 +1,22 @@ +getMethod()) { + $token = base64_encode(uniqid()); + + $response->setStatusCode(Response::HTTP_OK); + $response->setData(['auth-token' => $token]); +} + +$response->send(); diff --git a/plugin/xapi/cmi5/view.php b/plugin/xapi/cmi5/view.php new file mode 100644 index 000000000..dc0f08335 --- /dev/null +++ b/plugin/xapi/cmi5/view.php @@ -0,0 +1,84 @@ +find( + ToolLaunch::class, + $request->query->getInt('id') +); + +if (null === $toolLaunch + || 'cmi5' !== $toolLaunch->getActivityType() +) { + header('Location: '.api_get_course_url()); + exit; +} + +$plugin = XApiPlugin::create(); +$course = api_get_course_entity(); +$session = api_get_session_entity(); +$cidReq = api_get_cidreq(); +$user = api_get_user_entity(api_get_user_id()); +$interfaceLanguage = api_get_interface_language(); + +$itemsRepo = $em->getRepository(Cmi5Item::class); + +$query = $em->createQueryBuilder() + ->select('item') + ->from(Cmi5Item::class, 'item') + ->where('item.tool = :tool') + ->setParameter('tool', $toolLaunch->getId()) + ->getQuery(); + +$tocHtml = $itemsRepo->buildTree( + $query->getArrayResult(), + [ + 'decorate' => true, + 'rootOpen' => '

      ', + 'rootClose' => '
    ', + 'childOpen' => '
  • ', + 'childClose' => '
  • ', + 'nodeDecorator' => function ($node) use ($interfaceLanguage, $cidReq, $toolLaunch) { + $titleMap = LanguageMap::create($node['title']); + $title = XApiPlugin::extractVerbInLanguage($titleMap, $interfaceLanguage); + + if ('block' === $node['type']) { + return Display::page_subheader($title, null, 'h4'); + } + + return Display::url( + $title, + "launch.php?tool={$toolLaunch->getId()}&id={$node['id']}&$cidReq", + [ + 'target' => 'ifr_content', + 'class' => 'text-left btn-link', + ] + ); + }, + ] +); + +$webPluginPath = api_get_path(WEB_PLUGIN_PATH); + +$htmlHeadXtra[] = api_get_css($webPluginPath.'xapi/assets/css/cmi5_launch.css'); +$htmlHeadXtra[] = api_get_js_simple($webPluginPath.'xapi/assets/js/cmi5_launch.js'); + +$view = new Template('', false, false, true, true, false); +$view->assign('tool', $toolLaunch); +$view->assign('toc_html', $tocHtml); +$view->assign('content', $view->fetch('xapi/views/cmi5_launch.twig')); +$view->display_no_layout_template(); diff --git a/plugin/xapi/cron/send_statements.php b/plugin/xapi/cron/send_statements.php new file mode 100644 index 000000000..21cfc2bcb --- /dev/null +++ b/plugin/xapi/cron/send_statements.php @@ -0,0 +1,79 @@ +getRepository(SharedStatement::class) + ->findBy( + ['uuid' => null, 'sent' => false], + null, + 100 + ); +$countNotSent = count($notSentSharedStatements); + +if ($countNotSent > 0) { + echo '['.time().'] Trying to send '.$countNotSent.' statements to LRS'.PHP_EOL; + + $client = XApiPlugin::create()->getXapiStatementCronClient(); + + /** @var SharedStatement $notSentSharedStatement */ + foreach ($notSentSharedStatements as $notSentSharedStatement) { + $notSentStatement = $statementSerializer->deserializeStatement( + json_encode($notSentSharedStatement->getStatement()) + ); + + if (null == $notSentStatement->getId()) { + $notSentStatement = $notSentStatement->withId( + StatementId::fromUuid(Uuid::uuid4()) + ); + } + + try { + echo '['.time()."] Sending shared statement ({$notSentSharedStatement->getId()})"; + + $sentStatement = $client->storeStatement($notSentStatement); + + echo "\t\tStatement ID received: \"{$sentStatement->getId()->getValue()}\""; + } catch (ConflictException $e) { + echo $e->getMessage().PHP_EOL; + + continue; + } catch (XApiException $e) { + echo $e->getMessage().PHP_EOL; + + continue; + } + + $notSentSharedStatement + ->setUuid($sentStatement->getId()->getValue()) + ->setSent(true); + + $em->persist($notSentSharedStatement); + + echo "\t\tShared statement updated".PHP_EOL; + } + + $em->flush(); +} else { + echo 'No statements to process.'.PHP_EOL; +} diff --git a/plugin/xapi/install.php b/plugin/xapi/install.php new file mode 100644 index 000000000..c939bf532 --- /dev/null +++ b/plugin/xapi/install.php @@ -0,0 +1,5 @@ +install(); diff --git a/plugin/xapi/lang/english.php b/plugin/xapi/lang/english.php new file mode 100644 index 000000000..bee4d9a2b --- /dev/null +++ b/plugin/xapi/lang/english.php @@ -0,0 +1,56 @@ +This is generated automatically by Chamilo LMS. Don\'t replace it.'; +$strings['lrs_url'] = 'LRS endpoint'; +$strings['lrs_url_help'] = 'Base URL of the LRS'; +$strings['lrs_auth_username'] = 'LRS user'; +$strings['lrs_auth_username_help'] = 'Username for basic HTTP authentication'; +$strings['lrs_auth_password'] = 'LRS password'; +$strings['lrs_auth_password_help'] = 'Password for basic HTTP authentication'; +$strings['cron_lrs_url'] = 'Cron: LRS endpoint'; +$strings['cron_lrs_url_help'] = 'Alternative base URL of the LRS for the cron process'; +$strings['cron_lrs_auth_username'] = 'Cron: LRS user'; +$strings['cron_lrs_auth_username_help'] = 'Alternative username for basic HTTP authentication for the cron process'; +$strings['cron_lrs_auth_password'] = 'Cron: LRS password'; +$strings['cron_lrs_auth_password_help'] = 'Alternative password for basic HTTP authentication for the cron process'; +$strings['lrs_lp_item_viewed_active'] = 'Learning path item viewed'; +$strings['lrs_lp_end_active'] = 'Learning path ended'; +$strings['lrs_quiz_active'] = 'Quiz ended'; +$strings['lrs_quiz_question_active'] = 'Quiz question answered'; +$strings['lrs_portfolio_active'] = 'Portfolio events'; + +$strings['NoActivities'] = 'No activities added yet'; +$strings['ActivityTitle'] = 'Activity'; +$strings['AddActivity'] = 'Add activity'; +$strings['TinCanPackage'] = 'TinCan package (zip)'; +$strings['Cmi5Package'] = 'Cmi5 package (zip)'; +$strings['OnlyZipAllowed'] = 'Only ZIP file allowed (.zip).'; +$strings['ActivityImported'] = 'Activity imported.'; +$strings['EditActivity'] = 'Edit activity'; +$strings['ActivityUpdated'] = 'Activity updated'; +$strings['ActivityLaunchUrl'] = 'Launch URL'; +$strings['ActivityId'] = 'Activity ID'; +$strings['ActivityType'] = 'Activity type'; +$strings['ActivityDeleted'] = 'Activity deleted'; +$strings['ActivityLaunch'] = 'Launch'; +$strings['ActivityFirstLaunch'] = 'First launch at'; +$strings['ActivityLastLaunch'] = 'Last launch at'; +$strings['LaunchNewAttempt'] = 'Launch new attempt'; +$strings['LrsConfiguration'] = 'LRS Configuration'; +$strings['Verb'] = 'Verb'; +$strings['Actor'] = 'Actor'; +$strings['ToolTinCan'] = 'Activities'; +$strings['Terminated'] = 'Terminated'; +$strings['Completed'] = 'Completed'; +$strings['Answered'] = 'Answered'; +$strings['Viewed'] = 'Viewed'; +$strings['ActivityAddedToLPCannotBeAccessed'] = 'This activity has been included in a learning path, so it cannot be accessed by students directly from here.'; +$strings['XApiPackage'] = 'XApi Package'; +$strings['TinCanAllowMultipleAttempts'] = 'Allow multiple attempts'; diff --git a/plugin/xapi/lang/french.php b/plugin/xapi/lang/french.php new file mode 100644 index 000000000..d53ea9794 --- /dev/null +++ b/plugin/xapi/lang/french.php @@ -0,0 +1,49 @@ +Cette valeur est générée automatiquement par Chamilo, ne la modifiez pas.'; +$strings['lrs_url'] = 'Point d\'entrée LRS'; +$strings['lrs_url_help'] = 'URL de base du LRS'; +$strings['lrs_auth_username'] = 'Utilisateur LRS'; +$strings['lrs_auth_username_help'] = 'Nom d\'utilisateur pour l\'authentification HTTP de base'; +$strings['lrs_auth_password'] = 'Mot de passe LRS'; +$strings['lrs_auth_password_help'] = 'Mot de passe pour l\'authentification HTTP de base'; +$strings['cron_lrs_url'] = 'Cron: LRS endpoint'; +$strings['cron_lrs_url_help'] = 'Alternative base URL of the LRS for the cron process'; +$strings['cron_lrs_auth_username'] = 'Cron: LRS user'; +$strings['cron_lrs_auth_username_help'] = 'Alternative username for basic HTTP authentication for the cron process'; +$strings['cron_lrs_auth_password'] = 'Cron: LRS password'; +$strings['cron_lrs_auth_password_help'] = 'Alternative password for basic HTTP authentication for the cron process'; +$strings['lrs_lp_item_viewed_active'] = 'Élément de parcours visionné'; +$strings['lrs_lp_end_active'] = 'Parcours terminé'; +$strings['lrs_quiz_active'] = 'Exercice terminé'; +$strings['lrs_quiz_question_active'] = 'Question d\'exercice répondue'; +$strings['lrs_portfolio_active'] = 'Événements de portfolio'; + +$strings['NoActivities'] = 'Aucune activité ajoutée pour l\'instant'; +$strings['ActivityTitle'] = 'Activité'; +$strings['AddActivity'] = 'Ajouter activité'; +$strings['TinCanPackage'] = 'Paquet TinCan (zip)'; +$strings['OnlyZipAllowed'] = 'Seuls les fichiers ZIP sont autorisés (.zip).'; +$strings['ActivityImported'] = 'Activité importée.'; +$strings['EditActivity'] = 'Éditer activité'; +$strings['ActivityUpdated'] = 'Activité mise à jour'; +$strings['ActivityLaunchUrl'] = 'URL de lancement'; +$strings['ActivityId'] = 'ID d\'activité'; +$strings['ActivityType'] = 'Type d\'activité'; +$strings['ActivityDeleted'] = 'Activité supprimée'; +$strings['ActivityLaunch'] = 'Lancer'; +$strings['ActivityFirstLaunch'] = 'Premier lancement à'; +$strings['ActivityLastLaunch'] = 'Dernier lancement à'; +$strings['LaunchNewAttempt'] = 'Lancer nouvelle tentative'; +$strings['LrsConfiguration'] = 'Configuration LRS'; +$strings['Verb'] = 'Verbe'; +$strings['Actor'] = 'Acteur'; +$strings['ToolTinCan'] = 'Activités'; +$strings['ActivityAddedToLPCannotBeAccessed'] = 'Cet activité fait partie d\'un parcours d\'apprentissage, il n\'est donc pas accessible par les étudiants depuis cette page'; diff --git a/plugin/xapi/lang/spanish.php b/plugin/xapi/lang/spanish.php new file mode 100644 index 000000000..2056da671 --- /dev/null +++ b/plugin/xapi/lang/spanish.php @@ -0,0 +1,56 @@ +Esto es generado automáticamente por Chamilo LMS. No reemplazarlo.'; +$strings['lrs_url'] = 'LRS endpoint'; +$strings['lrs_url_help'] = 'Base de la URL del LRS'; +$strings['lrs_auth_username'] = 'Usuario del LRS'; +$strings['lrs_auth_username_help'] = 'Usuario para autenticación con HTTP básica'; +$strings['lrs_auth_password'] = 'Contraseña del LRS'; +$strings['lrs_auth_password_help'] = 'Contraseña para autenticación con HTTP básica'; +$strings['cron_lrs_url'] = 'Cron: LRS endpoint'; +$strings['cron_lrs_url_help'] = 'Opcional. Base de la URL alternativa del LRS del proceso cron.'; +$strings['cron_lrs_auth_username'] = 'Cron: Usuario del LRS'; +$strings['cron_lrs_auth_username_help'] = 'Opcional. Usuario alternativo para autenticación con HTTP básica del proceso cron'; +$strings['cron_lrs_auth_password'] = 'Cron: Contraseña del LRS'; +$strings['cron_lrs_auth_password_help'] = 'Opcional. Contraseña alternativa para autenticación con HTTP básica del proceso cron'; +$strings['lrs_lp_item_viewed_active'] = 'Visualización de contenido de lección'; +$strings['lrs_lp_end_active'] = 'Finalización de lección'; +$strings['lrs_quiz_active'] = 'Finalización de ejercicio'; +$strings['lrs_quiz_question_active'] = 'Resolución de pregunta en ejercicio'; +$strings['lrs_portfolio_active'] = 'Eventos en portafolio'; + +$strings['NoActivities'] = 'No hay actividades aún'; +$strings['ActivityTitle'] = 'Actividad'; +$strings['AddActivity'] = 'Agregar actividad'; +$strings['TinCanPackage'] = 'Paquete TinCan (zip)'; +$strings['Cmi5Package'] = 'Paquete Cmi5(zip)'; +$strings['OnlyZipAllowed'] = 'Sólo archivos ZIP están permitidos (.zip).'; +$strings['ActivityImported'] = 'Actividad importada.'; +$strings['EditActivity'] = 'Editar actividad'; +$strings['ActivityUpdated'] = 'Actividad actualizada'; +$strings['ActivityLaunchUrl'] = 'URL de inicio'; +$strings['ActivityId'] = 'ID de actividad'; +$strings['ActivityType'] = 'Tipo de actividad'; +$strings['ActivityDeleted'] = 'Actividad eliminada'; +$strings['ActivityLaunch'] = 'Iniciar'; +$strings['ActivityFirstLaunch'] = 'Primer inicio'; +$strings['ActivityLastLaunch'] = 'Últimmo inicio'; +$strings['LaunchNewAttempt'] = 'Iniciar nuevo intento'; +$strings['LrsConfiguration'] = 'Configuración de LRS'; +$strings['Verb'] = 'Verbo'; +$strings['Actor'] = 'Actor'; +$strings['ToolTinCan'] = 'Actividades'; +$strings['Terminated'] = 'Terminó'; +$strings['Completed'] = 'Completó'; +$strings['Answered'] = 'Respondió'; +$strings['Viewed'] = 'Visualizó'; +$strings['ActivityAddedToLPCannotBeAccessed'] = 'Esta actividad ha sido incluida en una secuencia de aprendizaje, por lo cual no podrá ser accesible directamente por los estudiantes desde aquí.'; +$strings['XApiPackage'] = 'Paquete XApi'; +$strings['TinCanAllowMultipleAttempts'] = 'Permitir múltiples intentos'; diff --git a/plugin/xapi/lrs.php b/plugin/xapi/lrs.php new file mode 100644 index 000000000..4fe9df4fd --- /dev/null +++ b/plugin/xapi/lrs.php @@ -0,0 +1,12 @@ +send(); diff --git a/plugin/xapi/php-xapi/lrs-bundle/LICENSE b/plugin/xapi/php-xapi/lrs-bundle/LICENSE new file mode 100644 index 000000000..de1d823c3 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Christian Flothmann + +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/plugin/xapi/php-xapi/lrs-bundle/README.md b/plugin/xapi/php-xapi/lrs-bundle/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/plugin/xapi/php-xapi/lrs-bundle/composer.json b/plugin/xapi/php-xapi/lrs-bundle/composer.json new file mode 100644 index 000000000..874027f02 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/composer.json @@ -0,0 +1,51 @@ +{ + "name": "php-xapi/lrs-bundle", + "type": "symfony-bundle", + "description": "Experience API (xAPI) Learning Record Store (LRS) based on the Symfony Framework", + "keywords": ["xAPI", "Experience API", "Tin Can API", "LRS", "Learning Record Store", "Symfony", "bundle"], + "homepage": "https://github.com/php-xapi/lrs-bundle", + "license": "MIT", + "authors": [ + { + "name": "Christian Flothmann", + "homepage": "https://github.com/xabbuh" + }, + { + "name": "Jérôme Parmentier", + "homepage": "https://github.com/Lctrs" + } + ], + "require": { + "php": "^7.1", + "php-xapi/exception": "^0.1 || ^0.2", + "php-xapi/model": "^1.1 || ^2.0 || ^3.0", + "php-xapi/repository-api": "^0.3@dev || ^0.4@dev", + "php-xapi/serializer": "^1.0 || ^2.0", + "php-xapi/symfony-serializer": "^1.0 || ^2.0", + "symfony/config": "^3.4 || ^4.3", + "symfony/dependency-injection": "^3.4 || ^4.3", + "symfony/http-foundation": "^3.4 || ^4.3", + "symfony/http-kernel": "^3.4 || ^4.3" + }, + "require-dev": { + "phpspec/phpspec": "~2.3", + "php-xapi/json-test-fixtures": "^1.0 || ^2.0", + "php-xapi/test-fixtures": "^1.0.1", + "ramsey/uuid": "^2.9 || ^3.0" + }, + "autoload": { + "psr-4": { + "XApi\\LrsBundle\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "spec\\XApi\\LrsBundle\\": "spec/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + } + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php new file mode 100644 index 000000000..88469b337 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Controller; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Xabbuh\XApi\Common\Exception\NotFoundException; +use Xabbuh\XApi\Model\IRL; +use Xabbuh\XApi\Model\Statement; +use Xabbuh\XApi\Model\StatementId; +use Xabbuh\XApi\Model\StatementResult; +use Xabbuh\XApi\Serializer\StatementResultSerializerInterface; +use Xabbuh\XApi\Serializer\StatementSerializerInterface; +use XApi\LrsBundle\Model\StatementsFilterFactory; +use XApi\LrsBundle\Response\AttachmentResponse; +use XApi\LrsBundle\Response\MultipartResponse; +use XApi\Repository\Api\StatementRepositoryInterface; + +/** + * @author Jérôme Parmentier + */ +class StatementGetController +{ + protected static $getParameters = [ + 'statementId' => true, + 'voidedStatementId' => true, + 'agent' => true, + 'verb' => true, + 'activity' => true, + 'registration' => true, + 'related_activities' => true, + 'related_agents' => true, + 'since' => true, + 'until' => true, + 'limit' => true, + 'format' => true, + 'attachments' => true, + 'ascending' => true, + 'cursor' => true, + ]; + + protected $repository; + protected $statementSerializer; + protected $statementResultSerializer; + protected $statementsFilterFactory; + + public function __construct(StatementRepositoryInterface $repository, StatementSerializerInterface $statementSerializer, StatementResultSerializerInterface $statementResultSerializer, StatementsFilterFactory $statementsFilterFactory) + { + $this->repository = $repository; + $this->statementSerializer = $statementSerializer; + $this->statementResultSerializer = $statementResultSerializer; + $this->statementsFilterFactory = $statementsFilterFactory; + } + + /** + * @throws BadRequestHttpException if the query parameters does not comply with xAPI specification + * + * @return Response + */ + public function getStatement(Request $request) + { + $query = new ParameterBag(\array_intersect_key($request->query->all(), self::$getParameters)); + + $this->validate($query); + + $includeAttachments = $query->filter('attachments', false, FILTER_VALIDATE_BOOLEAN); + try { + if (($statementId = $query->get('statementId')) !== null) { + $statement = $this->repository->findStatementById(StatementId::fromString($statementId)); + + $response = $this->buildSingleStatementResponse($statement, $includeAttachments); + } elseif (($voidedStatementId = $query->get('voidedStatementId')) !== null) { + $statement = $this->repository->findVoidedStatementById(StatementId::fromString($voidedStatementId)); + + $response = $this->buildSingleStatementResponse($statement, $includeAttachments); + } else { + $statements = $this->repository->findStatementsBy($this->statementsFilterFactory->createFromParameterBag($query)); + + $response = $this->buildMultiStatementsResponse($statements, $query, $includeAttachments); + } + } catch (NotFoundException $e) { + $response = $this->buildMultiStatementsResponse([], $query) + ->setStatusCode(Response::HTTP_NOT_FOUND) + ->setContent(''); + } catch (\Exception $exception) { + $response = Response::create('', Response::HTTP_BAD_REQUEST); + } + + $now = new \DateTime(); + $response->headers->set('X-Experience-API-Consistent-Through', $now->format(\DateTime::ATOM)); + $response->headers->set('Content-Type', 'application/json'); + + return $response; + } + + /** + * @param bool $includeAttachments true to include the attachments in the response, false otherwise + * + * @return JsonResponse|MultipartResponse + */ + protected function buildSingleStatementResponse(Statement $statement, $includeAttachments = false) + { + $json = $this->statementSerializer->serializeStatement($statement); + + $response = new Response($json, 200); + + if ($includeAttachments) { + $response = $this->buildMultipartResponse($response, [$statement]); + } + + $response->setLastModified($statement->getStored()); + + return $response; + } + + /** + * @param Statement[] $statements + * @param bool $includeAttachments true to include the attachments in the response, false otherwise + * + * @return JsonResponse|MultipartResponse + */ + protected function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false) + { + $moreUrlPath = $statements ? $this->generateMoreIrl($query) : null; + + $json = $this->statementResultSerializer->serializeStatementResult( + new StatementResult($statements, $moreUrlPath) + ); + + $response = new Response($json, 200); + + if ($includeAttachments) { + $response = $this->buildMultipartResponse($response, $statements); + } + + return $response; + } + + /** + * @param Statement[] $statements + * + * @return MultipartResponse + */ + protected function buildMultipartResponse(JsonResponse $statementResponse, array $statements) + { + $attachmentsParts = []; + + foreach ($statements as $statement) { + foreach ((array) $statement->getAttachments() as $attachment) { + $attachmentsParts[] = new AttachmentResponse($attachment); + } + } + + return new MultipartResponse($statementResponse, $attachmentsParts); + } + + /** + * Validate the parameters. + * + * @throws BadRequestHttpException if the parameters does not comply with the xAPI specification + */ + protected function validate(ParameterBag $query) + { + $hasStatementId = $query->has('statementId'); + $hasVoidedStatementId = $query->has('voidedStatementId'); + + if ($hasStatementId && $hasVoidedStatementId) { + throw new BadRequestHttpException('Request must not have both statementId and voidedStatementId parameters at the same time.'); + } + + $hasAttachments = $query->has('attachments'); + $hasFormat = $query->has('format'); + $queryCount = $query->count(); + + if (($hasStatementId || $hasVoidedStatementId) && $hasAttachments && $hasFormat && $queryCount > 3) { + throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".'); + } + + if (($hasStatementId || $hasVoidedStatementId) && ($hasAttachments || $hasFormat) && $queryCount > 2) { + throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".'); + } + + if (($hasStatementId || $hasVoidedStatementId) && $queryCount > 1) { + throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".'); + } + } + + protected function generateMoreIrl(ParameterBag $query): IRL + { + $params = $query->all(); + $params['cursor'] = empty($params['cursor']) ? 1 : $params['cursor'] + 1; + + return IRL::fromString( + '/plugin/xapi/lrs.php/statements?'.http_build_query($params) + ); + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php new file mode 100644 index 000000000..224a44185 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php @@ -0,0 +1,27 @@ +setContent(''); + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php new file mode 100644 index 000000000..d42625417 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Controller; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Xabbuh\XApi\Common\Exception\NotFoundException; +use Xabbuh\XApi\Model\Statement; +use XApi\Repository\Api\StatementRepositoryInterface; + +/** + * @author Jérôme Parmentier + */ +final class StatementPostController +{ + /** + * @var StatementRepositoryInterface + */ + private $repository; + + public function __construct(StatementRepositoryInterface $repository) + { + $this->repository = $repository; + } + + public function postStatements(Request $request, array $statements): JsonResponse + { + $statementsToStore = []; + + /** @var Statement $statement */ + foreach ($statements as $statement) { + if (null === $statementId = $statement->getId()) { + $statementsToStore[] = $statement; + + continue; + } + + try { + $existingStatement = $this->repository->findStatementById($statement->getId()); + + if (!$existingStatement->equals($statement)) { + throw new ConflictHttpException('The new statement is not equal to an existing statement with the same id.'); + } + } catch (NotFoundException $e) { + $statementsToStore[] = $statement; + } + } + + $uuids = []; + + foreach ($statementsToStore as $statement) { + $uuids[] = $this->repository->storeStatement($statement, true)->getValue(); + } + + return new JsonResponse($uuids); + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php new file mode 100644 index 000000000..660f99b36 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Xabbuh\XApi\Common\Exception\NotFoundException; +use Xabbuh\XApi\Model\Statement; +use Xabbuh\XApi\Model\StatementId; +use XApi\Repository\Api\StatementRepositoryInterface; + +/** + * @author Christian Flothmann + */ +final class StatementPutController +{ + private $repository; + + public function __construct(StatementRepositoryInterface $repository) + { + $this->repository = $repository; + } + + public function putStatement(Request $request, Statement $statement): Response + { + if (null === $statementId = $request->query->get('statementId')) { + throw new BadRequestHttpException('Required statementId parameter is missing.'); + } + + try { + $id = StatementId::fromString($statementId); + } catch (\InvalidArgumentException $e) { + throw new BadRequestHttpException(sprintf('Parameter statementId ("%s") is not a valid UUID.', $statementId), $e); + } + + if (null !== $statement->getId() && !$id->equals($statement->getId())) { + throw new ConflictHttpException(sprintf('Id parameter ("%s") and statement id ("%s") do not match.', $id->getValue(), $statement->getId()->getValue())); + } + + try { + $existingStatement = $this->repository->findStatementById($id); + + if (!$existingStatement->equals($statement)) { + throw new ConflictHttpException('The new statement is not equal to an existing statement with the same id.'); + } + } catch (NotFoundException $e) { + $statement = $statement->withId($id); + + $this->repository->storeStatement($statement, true); + } + + return new Response('', 204); + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/Configuration.php b/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/Configuration.php new file mode 100644 index 000000000..40cfa8755 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/Configuration.php @@ -0,0 +1,35 @@ + + */ +final class Configuration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + + $treeBuilder + ->root('xapi_lrs') + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['type']) && in_array($v['type'], ['mongodb', 'orm']) && !isset($v['object_manager_service']); }) + ->thenInvalid('You need to configure the object manager service when the repository type is "mongodb" or orm".') + ->end() + ->children() + ->enumNode('type') + ->isRequired() + ->values(['in_memory', 'mongodb', 'orm']) + ->end() + ->scalarNode('object_manager_service')->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/XApiLrsExtension.php b/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/XApiLrsExtension.php new file mode 100644 index 000000000..e2ff9e6d1 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/DependencyInjection/XApiLrsExtension.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +/** + * @author Christian Flothmann + */ +final class XApiLrsExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('controller.xml'); + $loader->load('event_listener.xml'); + $loader->load('factory.xml'); + $loader->load('serializer.xml'); + + switch ($config['type']) { + case 'in_memory': + break; + case 'mongodb': + $loader->load('doctrine.xml'); + $loader->load('mongodb.xml'); + + $container->setAlias('xapi_lrs.doctrine.object_manager', $config['object_manager_service']); + $container->setAlias('xapi_lrs.repository.statement', 'xapi_lrs.repository.statement.doctrine'); + break; + case 'orm': + $loader->load('doctrine.xml'); + $loader->load('orm.xml'); + + $container->setAlias('xapi_lrs.doctrine.object_manager', $config['object_manager_service']); + $container->setAlias('xapi_lrs.repository.statement', 'xapi_lrs.repository.statement.doctrine'); + break; + } + } + + public function getAlias() + { + return 'xapi_lrs'; + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/AlternateRequestSyntaxListener.php b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/AlternateRequestSyntaxListener.php new file mode 100644 index 000000000..ef4a5a1ea --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/AlternateRequestSyntaxListener.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +/** + * @author Jérôme Parmentier + */ +class AlternateRequestSyntaxListener +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + + if (!$request->attributes->has('xapi_lrs.route')) { + return; + } + + if ('POST' !== $request->getMethod()) { + return; + } + + if (null === $method = $request->query->get('method')) { + return; + } + + if ($request->query->count() > 1) { + throw new BadRequestHttpException('Including other query parameters than "method" is not allowed. You have to send them as POST parameters inside the request body.'); + } + + $request->setMethod($method); + $request->query->remove('method'); + + if (null !== $content = $request->request->get('content')) { + $request->request->remove('content'); + + $request->initialize( + $request->query->all(), + $request->request->all(), + $request->attributes->all(), + $request->cookies->all(), + $request->files->all(), + $request->server->all(), + $content + ); + } + + foreach ($request->request as $key => $value) { + if (in_array($key, ['Authorization', 'X-Experience-API-Version', 'Content-Type', 'Content-Length', 'If-Match', 'If-None-Match'], true)) { + $request->headers->set($key, $value); + } else { + $request->query->set($key, $value); + } + + $request->request->remove($key); + } + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/ExceptionListener.php b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/ExceptionListener.php new file mode 100644 index 000000000..956f2f27e --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/ExceptionListener.php @@ -0,0 +1,17 @@ + + */ +class ExceptionListener +{ + public function onKernelException(GetResponseForExceptionEvent $event) + { + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/SerializerListener.php b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/SerializerListener.php new file mode 100644 index 000000000..3bdda2f66 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/SerializerListener.php @@ -0,0 +1,40 @@ + + */ +class SerializerListener +{ + private $statementSerializer; + + public function __construct(StatementSerializerInterface $statementSerializer) + { + $this->statementSerializer = $statementSerializer; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + if (!$request->attributes->has('xapi_lrs.route')) { + return; + } + + try { + switch ($request->attributes->get('xapi_serializer')) { + case 'statement': + $request->attributes->set('statement', $this->statementSerializer->deserializeStatement($request->getContent())); + break; + } + } catch (BaseSerializerException $e) { + throw new BadRequestHttpException(sprintf('The content of the request cannot be deserialized into a valid xAPI %s.', $request->attributes->get('xapi_serializer')), $e); + } + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/VersionListener.php b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/VersionListener.php new file mode 100644 index 000000000..f82af7595 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/EventListener/VersionListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + +/** + * @author Jérôme Parmentier + */ +class VersionListener +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + + if (!$request->attributes->has('xapi_lrs.route')) { + return; + } + + if (null === $version = $request->headers->get('X-Experience-API-Version')) { + throw new BadRequestHttpException('Missing required "X-Experience-API-Version" header.'); + } + + if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) { + if ('1.0' === $version) { + $request->headers->set('X-Experience-API-Version', '1.0.0'); + } + + return; + } + + throw new BadRequestHttpException(sprintf('xAPI version "%s" is not supported.', $version)); + } + + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!$event->getRequest()->attributes->has('xapi_lrs.route')) { + return; + } + + $headers = $event->getResponse()->headers; + + if (!$headers->has('X-Experience-API-Version')) { + $headers->set('X-Experience-API-Version', '1.0.3'); + } + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php b/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php new file mode 100644 index 000000000..7e1ba67f4 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Model; + +use Symfony\Component\HttpFoundation\ParameterBag; +use Xabbuh\XApi\Model\Activity; +use Xabbuh\XApi\Model\IRI; +use Xabbuh\XApi\Model\StatementsFilter; +use Xabbuh\XApi\Model\Verb; +use Xabbuh\XApi\Serializer\ActorSerializerInterface; + +/** + * @author Jérôme Parmentier + */ +class StatementsFilterFactory +{ + private $actorSerializer; + + public function __construct(ActorSerializerInterface $actorSerializer) + { + $this->actorSerializer = $actorSerializer; + } + + /** + * @return StatementsFilter + */ + public function createFromParameterBag(ParameterBag $parameters) + { + $filter = new StatementsFilter(); + + if (($actor = $parameters->get('agent')) !== null) { + $filter->byActor($this->actorSerializer->deserializeActor($actor)); + } + + if (($verbId = $parameters->get('verb')) !== null) { + $filter->byVerb(new Verb(IRI::fromString($verbId))); + } + + if (($activityId = $parameters->get('activity')) !== null) { + $filter->byActivity(new Activity(IRI::fromString($activityId))); + } + + if (($registration = $parameters->get('registration')) !== null) { + $filter->byRegistration($registration); + } + + if ($parameters->filter('related_activities', false, FILTER_VALIDATE_BOOLEAN)) { + $filter->enableRelatedActivityFilter(); + } else { + $filter->disableRelatedActivityFilter(); + } + + if ($parameters->filter('related_agents', false, FILTER_VALIDATE_BOOLEAN)) { + $filter->enableRelatedAgentFilter(); + } else { + $filter->disableRelatedAgentFilter(); + } + + if (($since = $parameters->get('since')) !== null) { + $filter->since(\DateTime::createFromFormat(\DateTime::ATOM, $since)); + } + + if (($until = $parameters->get('until')) !== null) { + $filter->until(\DateTime::createFromFormat(\DateTime::ATOM, $until)); + } + + if ($parameters->filter('ascending', false, FILTER_VALIDATE_BOOLEAN)) { + $filter->ascending(); + } else { + $filter->descending(); + } + + $filter->limit($parameters->getInt('limit')); + + return $filter; + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/controller.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/controller.xml new file mode 100644 index 000000000..053e487df --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/controller.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/doctrine.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/doctrine.xml new file mode 100644 index 000000000..1eb2760b6 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/doctrine.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/event_listener.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/event_listener.xml new file mode 100644 index 000000000..08ad38c13 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/event_listener.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/factory.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/factory.xml new file mode 100644 index 000000000..4c43d9db1 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/factory.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml new file mode 100644 index 000000000..a7c820478 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml new file mode 100644 index 000000000..27c4d2bc1 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml @@ -0,0 +1,29 @@ + + + + + xapi_lrs.controller.statement.put:putStatement + statement + + true + + + + + xapi_lrs.controller.statement.post:postStatement + statement + + true + + + + + xapi_lrs.controller.statement.get:getStatement + + true + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/serializer.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/serializer.xml new file mode 100644 index 000000000..2ce345a97 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/serializer.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Response/AttachmentResponse.php b/plugin/xapi/php-xapi/lrs-bundle/src/Response/AttachmentResponse.php new file mode 100644 index 000000000..860de13f0 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Response/AttachmentResponse.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Response; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Xabbuh\XApi\Model\Attachment; + +/** + * @author Jérôme Parmentier + */ +class AttachmentResponse extends Response +{ + protected $attachment; + + public function __construct(Attachment $attachment) + { + parent::__construct(null); + + $this->attachment = $attachment; + } + + /** + * {@inheritdoc} + */ + public function prepare(Request $request) + { + if (!$this->headers->has('Content-Type')) { + $this->headers->set('Content-Type', $this->attachment->getContentType()); + } + + $this->headers->set('Content-Transfer-Encoding', 'binary'); + $this->headers->set('X-Experience-API-Hash', $this->attachment->getSha2()); + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + public function sendContent() + { + throw new \LogicException('An AttachmentResponse is only meant to be part of a multipart Response.'); + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on an AttachmentResponse instance.'); + } + } + + /** + * {@inheritdoc} + * + * @return string|null + */ + public function getContent() + { + return $this->attachment->getContent(); + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Response/MultipartResponse.php b/plugin/xapi/php-xapi/lrs-bundle/src/Response/MultipartResponse.php new file mode 100644 index 000000000..a5cb9e1f5 --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Response/MultipartResponse.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle\Response; + +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @author Jérôme Parmentier + */ +class MultipartResponse extends Response +{ + protected $subtype; + protected $boundary; + protected $statementPart; + /** + * @var Response[] + */ + protected $parts; + + /** + * @param AttachmentResponse[] $attachmentsParts + * @param int $status + * @param string|null $subtype + */ + public function __construct(JsonResponse $statementPart, array $attachmentsParts = [], $status = 200, array $headers = [], $subtype = null) + { + parent::__construct(null, $status, $headers); + + if (null === $subtype) { + $subtype = 'mixed'; + } + + $this->subtype = $subtype; + $this->boundary = uniqid('', true); + $this->statementPart = $statementPart; + + $this->setAttachmentsParts($attachmentsParts); + } + + /** + * @return $this + */ + public function addAttachmentPart(AttachmentResponse $part) + { + if ($part->getContent() !== null) { + $this->parts[] = $part; + } + + return $this; + } + + /** + * @param AttachmentResponse[] $attachmentsParts + * + * @return $this + */ + public function setAttachmentsParts(array $attachmentsParts) + { + $this->parts = [$this->statementPart]; + + foreach ($attachmentsParts as $part) { + $this->addAttachmentPart($part); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function prepare(Request $request) + { + foreach ($this->parts as $part) { + $part->prepare($request); + } + + $this->headers->set('Content-Type', sprintf('multipart/%s; boundary="%s"', $this->subtype, $this->boundary)); + $this->headers->set('Transfer-Encoding', 'chunked'); + + return parent::prepare($request); + } + + /** + * {@inheritdoc} + */ + public function sendContent() + { + $content = ''; + foreach ($this->parts as $part) { + $content .= sprintf('--%s', $this->boundary)."\r\n"; + $content .= $part->headers."\r\n"; + $content .= $part->getContent(); + $content .= "\r\n"; + } + + $content .= sprintf('--%s--', $this->boundary)."\r\n"; + + echo $content; + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a MultipartResponse instance.'); + } + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php b/plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php new file mode 100644 index 000000000..33d7775ff --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\LrsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use XApi\LrsBundle\DependencyInjection\XApiLrsExtension; + +/** + * @author Christian Flothmann + */ +class XApiLrsBundle extends Bundle +{ + public function getContainerExtension() + { + return new XApiLrsExtension(); + } +} diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE b/plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE new file mode 100644 index 000000000..de1d823c3 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Christian Flothmann + +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/plugin/xapi/php-xapi/repository-doctrine-orm/README.md b/plugin/xapi/php-xapi/repository-doctrine-orm/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/composer.json b/plugin/xapi/php-xapi/repository-doctrine-orm/composer.json new file mode 100644 index 000000000..dace61cba --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/composer.json @@ -0,0 +1,41 @@ +{ + "name": "php-xapi/repository-doctrine-orm", + "description": "Doctrine based ORM implementations of an Experience API (xAPI) repository", + "keywords": ["xAPI", "Tin Can API", "Experience API", "storage", "database", "repository", "entity", "Doctrine", "ORM"], + "homepage": "https://github.com/php-xapi/repository-orm/", + "license": "MIT", + "authors": [ + { + "name": "Christian Flothmann", + "homepage": "https://github.com/xabbuh" + } + ], + "require": { + "php": "^5.6 || ^7.0", + "doctrine/orm": "^2.3", + "php-xapi/repository-api": "^0.4", + "php-xapi/repository-doctrine": "^0.4" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.4 || ^4.0" + }, + "provide": { + "php-xapi/repository-implementation": "0.3" + }, + "autoload": { + "psr-4": { + "XApi\\Repository\\ORM\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "XApi\\Repository\\ORM\\Tests\\": "tests/" + } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + } + } +} diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Actor.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Actor.orm.xml new file mode 100644 index 000000000..040148a09 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Actor.orm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Attachment.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Attachment.orm.xml new file mode 100644 index 000000000..4fb517de6 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Attachment.orm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Context.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Context.orm.xml new file mode 100644 index 000000000..a33661e1c --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Context.orm.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Extensions.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Extensions.orm.xml new file mode 100644 index 000000000..efec34632 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Extensions.orm.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Result.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Result.orm.xml new file mode 100644 index 000000000..d1ffe5305 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Result.orm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Statement.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Statement.orm.xml new file mode 100644 index 000000000..321d5b864 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Statement.orm.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml new file mode 100644 index 000000000..22464ce3f --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml new file mode 100644 index 000000000..2e2277c6d --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php new file mode 100644 index 000000000..a551eb1d5 --- /dev/null +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace XApi\Repository\ORM; + +use Doctrine\ORM\EntityRepository; +use XApi\Repository\Doctrine\Mapping\Context; +use XApi\Repository\Doctrine\Mapping\Statement; +use XApi\Repository\Doctrine\Repository\Mapping\StatementRepository as BaseStatementRepository; + +/** + * @author Christian Flothmann + */ +final class StatementRepository extends EntityRepository implements BaseStatementRepository +{ + /** + * {@inheritdoc} + */ + public function findStatement(array $criteria) + { + return parent::findOneBy($criteria); + } + + /** + * {@inheritdoc} + */ + public function findStatements(array $criteria) + { + if (!empty($criteria['registration'])) { + $contexts = $this->_em->getRepository(Context::class)->findBy([ + 'registration' => $criteria['registration'], + ]); + + $criteria['context'] = $contexts; + } + + unset( + $criteria['registration'], + $criteria['related_activities'], + $criteria['related_agents'], + $criteria['ascending'], + $criteria['limit'] + ); + + return parent::findBy($criteria, ['created' => 'ASC']); + } + + /** + * {@inheritdoc} + */ + public function storeStatement(Statement $mappedStatement, $flush = true) + { + $this->_em->persist($mappedStatement); + + if ($flush) { + $this->_em->flush(); + } + } +} diff --git a/plugin/xapi/plugin.php b/plugin/xapi/plugin.php new file mode 100644 index 000000000..0e2770b43 --- /dev/null +++ b/plugin/xapi/plugin.php @@ -0,0 +1,5 @@ +get_info(); diff --git a/plugin/xapi/src/Entity/ActivityProfile.php b/plugin/xapi/src/Entity/ActivityProfile.php new file mode 100644 index 000000000..82f07523c --- /dev/null +++ b/plugin/xapi/src/Entity/ActivityProfile.php @@ -0,0 +1,93 @@ +id; + } + + public function setId(int $id): ActivityProfile + { + $this->id = $id; + + return $this; + } + + public function getProfileId(): string + { + return $this->profileId; + } + + public function setProfileId(string $profileId): ActivityProfile + { + $this->profileId = $profileId; + + return $this; + } + + public function getActivityId(): string + { + return $this->activityId; + } + + public function setActivityId(string $activityId): ActivityProfile + { + $this->activityId = $activityId; + + return $this; + } + + public function getDocumentData(): array + { + return $this->documentData; + } + + public function setDocumentData(array $documentData): ActivityProfile + { + $this->documentData = $documentData; + + return $this; + } +} diff --git a/plugin/xapi/src/Entity/ActivityState.php b/plugin/xapi/src/Entity/ActivityState.php new file mode 100644 index 000000000..f63d03b66 --- /dev/null +++ b/plugin/xapi/src/Entity/ActivityState.php @@ -0,0 +1,111 @@ +id; + } + + public function setId(int $id): ActivityState + { + $this->id = $id; + + return $this; + } + + public function getStateId(): string + { + return $this->stateId; + } + + public function setStateId(string $stateId): ActivityState + { + $this->stateId = $stateId; + + return $this; + } + + public function getActivityId(): string + { + return $this->activityId; + } + + public function setActivityId(string $activityId): ActivityState + { + $this->activityId = $activityId; + + return $this; + } + + public function getAgent(): array + { + return $this->agent; + } + + public function setAgent(array $agent): ActivityState + { + $this->agent = $agent; + + return $this; + } + + public function getDocumentData(): array + { + return $this->documentData; + } + + public function setDocumentData(array $documentData): ActivityState + { + $this->documentData = $documentData; + + return $this; + } +} diff --git a/plugin/xapi/src/Entity/Cmi5Item.php b/plugin/xapi/src/Entity/Cmi5Item.php new file mode 100644 index 000000000..c3f86deea --- /dev/null +++ b/plugin/xapi/src/Entity/Cmi5Item.php @@ -0,0 +1,372 @@ +children = new ArrayCollection(); + } + + public function getId(): int + { + return $this->id; + } + + public function setId(int $id): Cmi5Item + { + $this->id = $id; + + return $this; + } + + public function getIdentifier(): string + { + return $this->identifier; + } + + public function setIdentifier(string $identifier): Cmi5Item + { + $this->identifier = $identifier; + + return $this; + } + + public function getType(): string + { + return $this->type; + } + + public function setType(string $type): Cmi5Item + { + $this->type = $type; + + return $this; + } + + public function getTitle(): array + { + return $this->title; + } + + public function setTitle(array $title): Cmi5Item + { + $this->title = $title; + + return $this; + } + + public function getDescription(): array + { + return $this->description; + } + + public function setDescription(array $description): Cmi5Item + { + $this->description = $description; + + return $this; + } + + public function getUrl(): ?string + { + return $this->url; + } + + public function setUrl(?string $url): Cmi5Item + { + $this->url = $url; + + return $this; + } + + public function getActivityType(): ?string + { + return $this->activityType; + } + + public function setActivityType(?string $activityType): Cmi5Item + { + $this->activityType = $activityType; + + return $this; + } + + public function getLaunchMethod(): ?string + { + return $this->launchMethod; + } + + public function setLaunchMethod(?string $launchMethod): Cmi5Item + { + $this->launchMethod = $launchMethod; + + return $this; + } + + public function getMoveOn(): ?string + { + return $this->moveOn; + } + + public function setMoveOn(?string $moveOn): Cmi5Item + { + $this->moveOn = $moveOn; + + return $this; + } + + public function getMasteryScore(): ?float + { + return $this->masteryScore; + } + + public function setMasteryScore(?float $masteryScore): Cmi5Item + { + $this->masteryScore = $masteryScore; + + return $this; + } + + public function getLaunchParameters(): ?string + { + return $this->launchParameters; + } + + public function setLaunchParameters(?string $launchParameters): Cmi5Item + { + $this->launchParameters = $launchParameters; + + return $this; + } + + public function getEntitlementKey(): ?string + { + return $this->entitlementKey; + } + + public function setEntitlementKey(?string $entitlementKey): Cmi5Item + { + $this->entitlementKey = $entitlementKey; + + return $this; + } + + /** + * @return \Chamilo\PluginBundle\Entity\XApi\Cmi5Item|null + */ + public function getParent(): ?Cmi5Item + { + return $this->parent; + } + + /** + * @param \Chamilo\PluginBundle\Entity\XApi\Cmi5Item|null $parent + */ + public function setParent(?Cmi5Item $parent): Cmi5Item + { + $this->parent = $parent; + + return $this; + } + + public function getChildren(): ArrayCollection + { + return $this->children; + } + + public function setChildren(ArrayCollection $children): Cmi5Item + { + $this->children = $children; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(?string $status): Cmi5Item + { + $this->status = $status; + + return $this; + } + + /** + * @return \Chamilo\PluginBundle\Entity\XApi\ToolLaunch + */ + public function getTool(): ToolLaunch + { + return $this->tool; + } + + /** + * @param \Chamilo\PluginBundle\Entity\XApi\ToolLaunch $tool + */ + public function setTool(ToolLaunch $tool): Cmi5Item + { + $this->tool = $tool; + + return $this; + } + + /** + * @return \Chamilo\PluginBundle\Entity\XApi\Cmi5Item + */ + public function getRoot(): Cmi5Item + { + return $this->root; + } +} diff --git a/plugin/xapi/src/Entity/InternalLog.php b/plugin/xapi/src/Entity/InternalLog.php new file mode 100644 index 000000000..b9c807f94 --- /dev/null +++ b/plugin/xapi/src/Entity/InternalLog.php @@ -0,0 +1,227 @@ +id; + } + + public function getUser(): User + { + return $this->user; + } + + public function setUser(User $user): InternalLog + { + $this->user = $user; + + return $this; + } + + public function getStatementId(): string + { + return $this->statementId; + } + + public function setStatementId(string $statementId): InternalLog + { + $this->statementId = $statementId; + + return $this; + } + + public function getVerb(): string + { + return $this->verb; + } + + public function setVerb(string $verb): InternalLog + { + $this->verb = $verb; + + return $this; + } + + public function getObjectId(): string + { + return $this->objectId; + } + + public function setObjectId(string $objectId): InternalLog + { + $this->objectId = $objectId; + + return $this; + } + + public function getActivityName(): ?string + { + return $this->activityName; + } + + public function setActivityName(?string $activityName): InternalLog + { + $this->activityName = $activityName; + + return $this; + } + + public function getActivityDescription(): ?string + { + return $this->activityDescription; + } + + public function setActivityDescription(?string $activityDescription): InternalLog + { + $this->activityDescription = $activityDescription; + + return $this; + } + + public function getScoreScaled(): ?float + { + return $this->scoreScaled; + } + + public function setScoreScaled(?float $scoreScaled): InternalLog + { + $this->scoreScaled = $scoreScaled; + + return $this; + } + + public function getScoreRaw(): ?float + { + return $this->scoreRaw; + } + + public function setScoreRaw(?float $scoreRaw): InternalLog + { + $this->scoreRaw = $scoreRaw; + + return $this; + } + + public function getScoreMin(): ?float + { + return $this->scoreMin; + } + + public function setScoreMin(?float $scoreMin): InternalLog + { + $this->scoreMin = $scoreMin; + + return $this; + } + + public function getScoreMax(): ?float + { + return $this->scoreMax; + } + + public function setScoreMax(?float $scoreMax): InternalLog + { + $this->scoreMax = $scoreMax; + + return $this; + } + + public function getCreatedAt(): ?DateTime + { + return $this->createdAt; + } + + public function setCreatedAt(?DateTime $createdAt): InternalLog + { + $this->createdAt = $createdAt; + + return $this; + } +} diff --git a/plugin/xapi/src/Entity/LrsAuth.php b/plugin/xapi/src/Entity/LrsAuth.php new file mode 100644 index 000000000..69cc839ea --- /dev/null +++ b/plugin/xapi/src/Entity/LrsAuth.php @@ -0,0 +1,104 @@ +id; + } + + public function getUsername(): string + { + return $this->username; + } + + public function setUsername(string $username): LrsAuth + { + $this->username = $username; + + return $this; + } + + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): LrsAuth + { + $this->password = $password; + + return $this; + } + + public function isEnabled(): bool + { + return $this->enabled; + } + + public function setEnabled(bool $enabled): LrsAuth + { + $this->enabled = $enabled; + + return $this; + } + + public function getCreatedAt(): \DateTime + { + return $this->createdAt; + } + + public function setCreatedAt(\DateTime $createdAt): LrsAuth + { + $this->createdAt = $createdAt; + + return $this; + } +} diff --git a/plugin/xapi/src/Entity/Repository/ToolLaunchRepository.php b/plugin/xapi/src/Entity/Repository/ToolLaunchRepository.php new file mode 100644 index 000000000..0446c9fc0 --- /dev/null +++ b/plugin/xapi/src/Entity/Repository/ToolLaunchRepository.php @@ -0,0 +1,85 @@ + $course, + 'session' => null, + ]; + + if ($session) { + $criteria['session'] = $session; + } + + return $this->findBy($criteria, $orderBy, $limit, $start); + } + + public function countByCourseAndSession(Course $course, Session $session = null, $filteredForStudent = false): int + { + $qb = $this->createQueryBuilder('tl'); + $qb->select($qb->expr()->count('tl')) + ->where($qb->expr()->eq('tl.course', ':course')) + ->setParameter('course', $course); + + if ($session) { + $qb->andWhere($qb->expr()->eq('tl.session', ':session')) + ->setParameter('session', $session); + } else { + $qb->andWhere($qb->expr()->isNull('tl.session')); + } + + if ($filteredForStudent) { + $qb + ->leftJoin( + CLpItem::class, + 'lpi', + Join::WITH, + "tl.id = lpi.path AND tl.course = lpi.cId AND lpi.itemType = 'xapi'" + ) + ->andWhere($qb->expr()->isNull('lpi.path')); + } + + $query = $qb->getQuery(); + + return (int) $query->getSingleScalarResult(); + } + + public function wasAddedInLp(ToolLaunch $toolLaunch): int + { + $qb = $this->getEntityManager()->createQueryBuilder(); + + return (int) $qb->select($qb->expr()->count('lp')) + ->from(CLp::class, 'lp') + ->innerJoin(CLpItem::class, 'lpi', Join::WITH, 'lp.id = lpi.lpId') + ->where('lpi.itemType = :type') + ->andWhere('lpi.path = :tool_id') + ->setParameter('type', TOOL_XAPI) + ->setParameter('tool_id', $toolLaunch->getId()) + ->getQuery() + ->getSingleScalarResult(); + } +} diff --git a/plugin/xapi/src/Entity/SharedStatement.php b/plugin/xapi/src/Entity/SharedStatement.php new file mode 100644 index 000000000..5c4d81caa --- /dev/null +++ b/plugin/xapi/src/Entity/SharedStatement.php @@ -0,0 +1,105 @@ +statement = $statement; + $this->uuid = $uuid; + $this->sent = $sent; + } + + public function getId(): int + { + return $this->id; + } + + public function getUuid(): ?string + { + return $this->uuid; + } + + public function setUuid(?string $uuid): SharedStatement + { + $this->uuid = $uuid; + + return $this; + } + + public function getStatement(): array + { + return $this->statement; + } + + public function setStatement(array $statement): SharedStatement + { + $this->statement = $statement; + + return $this; + } + + public function isSent(): bool + { + return $this->sent; + } + + public function setSent(bool $sent): SharedStatement + { + $this->sent = $sent; + + return $this; + } +} diff --git a/plugin/xapi/src/Entity/ToolLaunch.php b/plugin/xapi/src/Entity/ToolLaunch.php new file mode 100644 index 000000000..6d9d4f5b8 --- /dev/null +++ b/plugin/xapi/src/Entity/ToolLaunch.php @@ -0,0 +1,295 @@ +allowMultipleAttempts = true; + $this->items = new ArrayCollection(); + } + + public function getId(): int + { + return $this->id; + } + + public function setId(int $id): ToolLaunch + { + $this->id = $id; + + return $this; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): ToolLaunch + { + $this->title = $title; + + return $this; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): ToolLaunch + { + $this->description = $description; + + return $this; + } + + public function getCourse(): Course + { + return $this->course; + } + + public function setCourse(Course $course): ToolLaunch + { + $this->course = $course; + + return $this; + } + + public function getSession(): ?Session + { + return $this->session; + } + + public function setSession(?Session $session): ToolLaunch + { + $this->session = $session; + + return $this; + } + + public function getLaunchUrl(): string + { + return $this->launchUrl; + } + + public function setLaunchUrl(string $launchUrl): ToolLaunch + { + $this->launchUrl = $launchUrl; + + return $this; + } + + public function getActivityId(): ?string + { + return $this->activityId; + } + + public function setActivityId(?string $activityId): ToolLaunch + { + $this->activityId = $activityId; + + return $this; + } + + public function getCreatedAt(): DateTime + { + return $this->createdAt; + } + + public function setCreatedAt(DateTime $createdAt): ToolLaunch + { + $this->createdAt = $createdAt; + + return $this; + } + + public function getActivityType(): ?string + { + return $this->activityType; + } + + public function setActivityType(?string $activityType): ToolLaunch + { + $this->activityType = $activityType; + + return $this; + } + + public function isAllowMultipleAttempts(): bool + { + return $this->allowMultipleAttempts; + } + + public function setAllowMultipleAttempts(bool $allowMultipleAttempts): ToolLaunch + { + $this->allowMultipleAttempts = $allowMultipleAttempts; + + return $this; + } + + public function getLrsUrl(): ?string + { + return $this->lrsUrl; + } + + public function setLrsUrl(?string $lrsUrl): ToolLaunch + { + $this->lrsUrl = $lrsUrl; + + return $this; + } + + public function getLrsAuthUsername(): ?string + { + return $this->lrsAuthUsername; + } + + public function setLrsAuthUsername(?string $lrsAuthUsername): ToolLaunch + { + $this->lrsAuthUsername = $lrsAuthUsername; + + return $this; + } + + public function getLrsAuthPassword(): ?string + { + return $this->lrsAuthPassword; + } + + public function setLrsAuthPassword(?string $lrsAuthPassword): ToolLaunch + { + $this->lrsAuthPassword = $lrsAuthPassword; + + return $this; + } + + public function getItems(): ArrayCollection + { + return $this->items; + } + + /** + * @param \Chamilo\PluginBundle\Entity\XApi\Cmi5Item $cmi5Item + * + * @return $this + */ + public function addItem(Cmi5Item $cmi5Item) + { + $cmi5Item->setTool($this); + + $this->items->add($cmi5Item); + + return $this; + } +} diff --git a/plugin/xapi/src/Hook/XApiActivityHookObserver.php b/plugin/xapi/src/Hook/XApiActivityHookObserver.php new file mode 100644 index 000000000..c622e5eee --- /dev/null +++ b/plugin/xapi/src/Hook/XApiActivityHookObserver.php @@ -0,0 +1,66 @@ +plugin = XApiPlugin::create(); + } + + /** + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * + * @return \Chamilo\PluginBundle\Entity\XApi\SharedStatement|null + */ + protected function saveSharedStatement(Statement $statement) + { + $statementSerialized = $this->serializeStatement($statement); + + $sharedStmt = new SharedStatement( + json_decode($statementSerialized, true) + ); + + $em = Database::getManager(); + $em->persist($sharedStmt); + $em->flush(); + + return $sharedStmt; + } + + /** + * Serialize a statement to JSON. + * + * @return string + */ + private function serializeStatement(Statement $statement) + { + $serializer = Serializer::createSerializer(); + $statementSerializer = new StatementSerializer($serializer); + + return $statementSerializer->serializeStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiCreateCourseHookObserver.php b/plugin/xapi/src/Hook/XApiCreateCourseHookObserver.php new file mode 100644 index 000000000..dd5ca328b --- /dev/null +++ b/plugin/xapi/src/Hook/XApiCreateCourseHookObserver.php @@ -0,0 +1,37 @@ +getEventData(); + + $type = $data['type']; + $courseInfo = $data['course_info']; + + $plugin = XApiPlugin::create(); + + if (HOOK_EVENT_TYPE_POST == $type) { + $plugin->addCourseToolForTinCan($courseInfo['real_id']); + } + } +} diff --git a/plugin/xapi/src/Hook/XApiLearningPathEndHookObserver.php b/plugin/xapi/src/Hook/XApiLearningPathEndHookObserver.php new file mode 100644 index 000000000..5993cfacc --- /dev/null +++ b/plugin/xapi/src/Hook/XApiLearningPathEndHookObserver.php @@ -0,0 +1,26 @@ +getEventData(); + $em = Database::getManager(); + + $lpView = $em->find('ChamiloCourseBundle:CLpView', $data['lp_view_id']); + $lp = $em->find('ChamiloCourseBundle:CLp', $lpView->getLpId()); + + $learningPathEnded = new LearningPathCompleted($lpView, $lp); + + $statement = $learningPathEnded->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiLearningPathItemViewedHookObserver.php b/plugin/xapi/src/Hook/XApiLearningPathItemViewedHookObserver.php new file mode 100644 index 000000000..676b504c3 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiLearningPathItemViewedHookObserver.php @@ -0,0 +1,35 @@ +getEventData(); + $em = Database::getManager(); + + $lpItemView = $em->find('ChamiloCourseBundle:CLpItemView', $data['item_view_id']); + $lpItem = $em->find('ChamiloCourseBundle:CLpItem', $lpItemView->getLpItemId()); + + if ('quiz' == $lpItem->getItemType()) { + return null; + } + + $lpView = $em->find('ChamiloCourseBundle:CLpView', $lpItemView->getLpViewId()); + + $lpItemViewed = new LearningPathItemViewed($lpItemView, $lpItem, $lpView); + + $statement = $lpItemViewed->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioCommentEditedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioCommentEditedHookObserver.php new file mode 100644 index 000000000..4bd43bfd0 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioCommentEditedHookObserver.php @@ -0,0 +1,19 @@ +getEventData()['comment']; + + $statement = (new PortfolioCommentEdited($comment))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioCommentScoredHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioCommentScoredHookObserver.php new file mode 100644 index 000000000..3dcd0de5d --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioCommentScoredHookObserver.php @@ -0,0 +1,19 @@ +getEventData()['comment']; + + $statement = (new PortfolioCommentScored($comment))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioDownloadedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioDownloadedHookObserver.php new file mode 100644 index 000000000..b992006c1 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioDownloadedHookObserver.php @@ -0,0 +1,17 @@ +getEventData()['owner']; + + $statement = (new PortfolioDownloaded($owner))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemAddedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemAddedHookObserver.php new file mode 100644 index 000000000..5738a1f4f --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemAddedHookObserver.php @@ -0,0 +1,25 @@ +getEventData()['portfolio']; + + $portfolioItemShared = new PortfolioItemShared($item); + + $statement = $portfolioItemShared->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemCommentedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemCommentedHookObserver.php new file mode 100644 index 000000000..32bc4a8d8 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemCommentedHookObserver.php @@ -0,0 +1,25 @@ +getEventData()['comment']; + + $portfolioItemCommented = new PortfolioItemCommented($comment); + + $statement = $portfolioItemCommented->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemEditedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemEditedHookObserver.php new file mode 100644 index 000000000..a5535c2a2 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemEditedHookObserver.php @@ -0,0 +1,19 @@ +getEventData()['item']; + + $statement = (new PortfolioItemEdited($item))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemHighlightedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemHighlightedHookObserver.php new file mode 100644 index 000000000..9a7092329 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemHighlightedHookObserver.php @@ -0,0 +1,22 @@ +getEventData()['item']; + + $statement = (new PortfolioItemHighlighted($item))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemScoredHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemScoredHookObserver.php new file mode 100644 index 000000000..bed2dfb94 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemScoredHookObserver.php @@ -0,0 +1,19 @@ +getEventData()['item']; + + $statement = (new PortfolioItemScored($item))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiPortfolioItemViewedHookObserver.php b/plugin/xapi/src/Hook/XApiPortfolioItemViewedHookObserver.php new file mode 100644 index 000000000..fbe525066 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiPortfolioItemViewedHookObserver.php @@ -0,0 +1,25 @@ +getEventData()['portfolio']; + + $statement = (new PortfolioItemViewed($item))->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiQuizEndHookObserver.php b/plugin/xapi/src/Hook/XApiQuizEndHookObserver.php new file mode 100644 index 000000000..c85407953 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiQuizEndHookObserver.php @@ -0,0 +1,29 @@ +getEventData(); + $em = Database::getManager(); + + $exe = $em->find('ChamiloCoreBundle:TrackEExercises', $data['exe_id']); + $quiz = $em->find('ChamiloCourseBundle:CQuiz', $exe->getExeExoId()); + + $quizCompleted = new QuizCompleted($exe, $quiz); + + $statement = $quizCompleted->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Hook/XApiQuizQuestionAnsweredHookObserver.php b/plugin/xapi/src/Hook/XApiQuizQuestionAnsweredHookObserver.php new file mode 100644 index 000000000..f16454255 --- /dev/null +++ b/plugin/xapi/src/Hook/XApiQuizQuestionAnsweredHookObserver.php @@ -0,0 +1,42 @@ +getEventData(); + + $em = Database::getManager(); + $attemptRepo = $em->getRepository(TrackEAttempt::class); + + $exe = $em->find(TrackEExercises::class, $data['exe_id']); + $question = $em->find(CQuizQuestion::class, $data['question']['id']); + $attempt = $attemptRepo->findOneBy( + [ + 'exeId' => $exe->getExeId(), + 'questionId' => $question->getId(), + ] + ); + $quiz = $em->find(CQuiz::class, $data['quiz']['id']); + + $quizQuestionAnswered = new QuizQuestionAnswered($attempt, $question, $quiz); + + $statement = $quizQuestionAnswered->generate(); + + $this->saveSharedStatement($statement); + } +} diff --git a/plugin/xapi/src/Importer/PackageImporter.php b/plugin/xapi/src/Importer/PackageImporter.php new file mode 100644 index 000000000..25a8b4b1f --- /dev/null +++ b/plugin/xapi/src/Importer/PackageImporter.php @@ -0,0 +1,67 @@ +packageFileInfo = $fileInfo; + $this->course = $course; + + $this->courseDirectoryPath = api_get_path(SYS_COURSE_PATH).$this->course->getDirectory(); + } + + /** + * @return \Chamilo\PluginBundle\XApi\Importer\XmlPackageImporter|\Chamilo\PluginBundle\XApi\Importer\ZipPackageImporter + */ + public static function create(array $fileInfo, Course $course) + { + if ('text/xml' === $fileInfo['type']) { + return new XmlPackageImporter($fileInfo, $course); + } + + return new ZipPackageImporter($fileInfo, $course); + } + + /** + * @throws \Exception + * + * @return mixed + */ + abstract public function import(): string; + + public function getPackageType(): string + { + return $this->packageType; + } +} diff --git a/plugin/xapi/src/Importer/XmlPackageImporter.php b/plugin/xapi/src/Importer/XmlPackageImporter.php new file mode 100644 index 000000000..2a48fddb3 --- /dev/null +++ b/plugin/xapi/src/Importer/XmlPackageImporter.php @@ -0,0 +1,29 @@ +packageFileInfo['name'], ['tincan.xml', 'cmi5.xml'])) { + throw new Exception('Invalid package'); + } + + $this->packageType = explode('.', $this->packageFileInfo['name'], 2)[0]; + + return $this->packageFileInfo['tmp_name']; + } +} diff --git a/plugin/xapi/src/Importer/ZipPackageImporter.php b/plugin/xapi/src/Importer/ZipPackageImporter.php new file mode 100644 index 000000000..ae5be37a2 --- /dev/null +++ b/plugin/xapi/src/Importer/ZipPackageImporter.php @@ -0,0 +1,88 @@ +packageFileInfo['tmp_name']); + $zipContent = $zipFile->listContent(); + + $packageSize = array_reduce( + $zipContent, + function ($accumulator, $zipEntry) { + if (preg_match('~.(php.*|phtml)$~i', $zipEntry['filename'])) { + throw new Exception("File \"{$zipEntry['filename']}\" contains a PHP script"); + } + + if (in_array($zipEntry['filename'], ['tincan.xml', 'cmi5.xml'])) { + $this->packageType = explode('.', $zipEntry['filename'], 2)[0]; + } + + return $accumulator + $zipEntry['size']; + } + ); + + if (empty($this->packageType)) { + throw new Exception('Invalid package'); + } + + $this->validateEnoughSpace($packageSize); + + $pathInfo = pathinfo($this->packageFileInfo['name']); + + $packageDirectoryPath = $this->generatePackageDirectory($pathInfo['filename']); + + $zipFile->extract($packageDirectoryPath); + + return "$packageDirectoryPath/{$this->packageType}.xml"; + } + + /** + * @throws \Exception + */ + protected function validateEnoughSpace(int $packageSize) + { + $courseSpaceQuota = DocumentManager::get_course_quota($this->course->getCode()); + + if (!enough_size($packageSize, $this->courseDirectoryPath, $courseSpaceQuota)) { + throw new Exception('Not enough space to storage package.'); + } + } + + private function generatePackageDirectory(string $name): string + { + $directoryPath = implode( + '/', + [ + $this->courseDirectoryPath, + $this->packageType, + api_replace_dangerous_char($name), + ] + ); + + $fs = new Filesystem(); + $fs->mkdir( + $directoryPath, + api_get_permissions_for_new_directories() + ); + + return $directoryPath; + } +} diff --git a/plugin/xapi/src/Lrs/AboutController.php b/plugin/xapi/src/Lrs/AboutController.php new file mode 100644 index 000000000..084f2ddae --- /dev/null +++ b/plugin/xapi/src/Lrs/AboutController.php @@ -0,0 +1,30 @@ + [ + '1.0.3', + '1.0.2', + '1.0.1', + '1.0.0', + ], + ]; + + return JsonResponse::create($json); + } +} diff --git a/plugin/xapi/src/Lrs/ActivitiesProfileController.php b/plugin/xapi/src/Lrs/ActivitiesProfileController.php new file mode 100644 index 000000000..bf12e82f0 --- /dev/null +++ b/plugin/xapi/src/Lrs/ActivitiesProfileController.php @@ -0,0 +1,78 @@ +httpRequest->query->get('profileId'); + $activityId = $this->httpRequest->query->get('activityId'); + + $em = \Database::getManager(); + $profileRepo = $em->getRepository(ActivityProfile::class); + + /** @var ActivityProfile $activityProfile */ + $activityProfile = $profileRepo->findOneBy( + [ + 'profileId' => $profileId, + 'activityId' => $activityId, + ] + ); + + if (empty($activityProfile)) { + return Response::create(null, Response::HTTP_NO_CONTENT); + } + + return Response::create( + json_encode($activityProfile->getDocumentData()) + ); + } + + public function head(): Response + { + return $this->get()->setContent(''); + } + + public function put(): Response + { + $profileId = $this->httpRequest->query->get('profileId'); + $activityId = $this->httpRequest->query->get('activityId'); + $documentData = $this->httpRequest->getContent(); + + $em = \Database::getManager(); + $profileRepo = $em->getRepository(ActivityProfile::class); + + /** @var ActivityProfile $activityProfile */ + $activityProfile = $profileRepo->findOneBy( + [ + 'profileId' => $profileId, + 'activityId' => $activityId, + ] + ); + + if (empty($activityProfile)) { + $activityProfile = new ActivityProfile(); + $activityProfile + ->setProfileId($profileId) + ->setActivityId($activityId); + } + + $activityProfile->setDocumentData(json_decode($documentData, true)); + + $em->persist($activityProfile); + $em->flush(); + + return Response::create(null, Response::HTTP_NO_CONTENT); + } +} diff --git a/plugin/xapi/src/Lrs/ActivitiesStateController.php b/plugin/xapi/src/Lrs/ActivitiesStateController.php new file mode 100644 index 000000000..6d05fc7b8 --- /dev/null +++ b/plugin/xapi/src/Lrs/ActivitiesStateController.php @@ -0,0 +1,120 @@ +httpRequest->query->get('agent'); + $activityId = $this->httpRequest->query->get('activityId'); + $stateId = $this->httpRequest->query->get('stateId'); + + $state = Database::select( + '*', + Database::get_main_table('xapi_activity_state'), + [ + 'where' => [ + 'state_id = ? AND activity_id = ? AND MD5(agent) = ?' => [ + Database::escape_string($stateId), + Database::escape_string($activityId), + md5($requestedAgent), + ], + ], + ], + 'first' + ); + + if (empty($state)) { + return JsonResponse::create([], Response::HTTP_NOT_FOUND); + } + + $requestedAgent = $serializer->deserialize( + $this->httpRequest->query->get('agent'), + Actor::class, + 'json' + ); + + /** @var Actor $stateAgent */ + $stateAgent = $serializer->deserialize( + $state['agent'], + Actor::class, + 'json' + ); + + if (!$stateAgent->equals($requestedAgent)) { + return JsonResponse::create([], Response::HTTP_NOT_FOUND); + } + + $documentData = json_decode($state['document_data'], true); + + return JsonResponse::create($documentData); + } + + public function head(): Response + { + return $this->get()->setContent(''); + } + + public function post(): Response + { + return $this->put(); + } + + public function put(): Response + { + $activityId = $this->httpRequest->query->get('activityId'); + $agent = $this->httpRequest->query->get('agent'); + $stateId = $this->httpRequest->query->get('stateId'); + $documentData = $this->httpRequest->getContent(); + + $state = Database::select( + 'id', + Database::get_main_table('xapi_activity_state'), + [ + 'where' => [ + 'state_id = ? AND activity_id = ? AND MD5(agent) = ?' => [ + Database::escape_string($stateId), + Database::escape_string($activityId), + md5($agent), + ], + ], + ], + 'first' + ); + + $em = Database::getManager(); + + if (empty($state)) { + $state = new ActivityState(); + $state + ->setActivityId($activityId) + ->setAgent(json_decode($agent, true)) + ->setStateId($stateId); + } else { + $state = $em->find(ActivityState::class, $state['id']); + } + + $state->setDocumentData(json_decode($documentData, true)); + + $em->persist($state); + $em->flush(); + + return Response::create('', Response::HTTP_NO_CONTENT); + } +} diff --git a/plugin/xapi/src/Lrs/BaseController.php b/plugin/xapi/src/Lrs/BaseController.php new file mode 100644 index 000000000..865b2b71b --- /dev/null +++ b/plugin/xapi/src/Lrs/BaseController.php @@ -0,0 +1,28 @@ +httpRequest = $httpRequest; + } +} diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php new file mode 100644 index 000000000..f409f6194 --- /dev/null +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -0,0 +1,221 @@ +request = HttpRequest::createFromGlobals(); + } + + public function send() + { + try { + $this->alternateRequestSyntax(); + + $controllerName = $this->getControllerName(); + $methodName = $this->getMethodName(); + + $response = $this->generateResponse($controllerName, $methodName); + } catch (XApiException $xApiException) { + $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); + } catch (HttpException $httpException) { + $response = HttpResponse::create( + $httpException->getMessage(), + $httpException->getStatusCode() + ); + } catch (\Exception $exception) { + $response = HttpResponse::create($exception->getMessage(), HttpResponse::HTTP_BAD_REQUEST); + } + + $response->headers->set('X-Experience-API-Version', '1.0.3'); + + $response->send(); + } + + /** + * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException + */ + private function validateAuth(): bool + { + if (!$this->request->headers->has('Authorization')) { + throw new AccessDeniedException(); + } + + $authHeader = $this->request->headers->get('Authorization'); + + $parts = explode('Basic ', $authHeader, 2); + + if (empty($parts[1])) { + throw new AccessDeniedException(); + } + + $authDecoded = base64_decode($parts[1]); + + $parts = explode(':', $authDecoded, 2); + + if (empty($parts) || count($parts) !== 2) { + throw new AccessDeniedException(); + } + + list($username, $password) = $parts; + + $auth = Database::getManager() + ->getRepository(LrsAuth::class) + ->findOneBy( + ['username' => $username, 'password' => $password, 'enabled' => true] + ); + + if (null == $auth) { + throw new AccessDeniedException(); + } + + return true; + } + + private function validateVersion() + { + $version = $this->request->headers->get('X-Experience-API-Version'); + + if (null === $version) { + throw new BadRequestHttpException('The "X-Experience-API-Version" header is required.'); + } + + if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) { + if ('1.0' === $version) { + $this->request->headers->set('X-Experience-API-Version', '1.0.0'); + } + + return; + } + + throw new BadRequestHttpException("The xAPI version \"$version\" is not supported."); + } + + private function getControllerName(): ?string + { + $segments = explode('/', $this->request->getPathInfo()); + $segments = array_filter($segments); + $segments = array_values($segments); + + if (empty($segments)) { + throw new BadRequestHttpException('Bad request'); + } + + $segments = array_map('ucfirst', $segments); + $controllerName = implode('', $segments).'Controller'; + + return "Chamilo\\PluginBundle\\XApi\Lrs\\$controllerName"; + } + + private function getMethodName(): string + { + $method = $this->request->getMethod(); + + return strtolower($method); + } + + /** + * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException + */ + private function generateResponse(string $controllerName, string $methodName): HttpResponse + { + if (!class_exists($controllerName) + || !method_exists($controllerName, $methodName) + ) { + throw new NotFoundHttpException(); + } + + if ($controllerName !== AboutController::class) { + $this->validateAuth(); + $this->validateVersion(); + } + + /** @var HttpResponse $response */ + $response = call_user_func( + [ + new $controllerName($this->request), + $methodName, + ] + ); + + return $response; + } + + private function alternateRequestSyntax() + { + if ('POST' !== $this->request->getMethod()) { + return; + } + + if (null === $method = $this->request->query->get('method')) { + return; + } + + if ($this->request->query->count() > 1) { + throw new BadRequestHttpException('Including other query parameters than "method" is not allowed. You have to send them as POST parameters inside the request body.'); + } + + $this->request->setMethod($method); + $this->request->query->remove('method'); + + if (null !== $content = $this->request->request->get('content')) { + $this->request->request->remove('content'); + + $this->request->initialize( + $this->request->query->all(), + $this->request->request->all(), + $this->request->attributes->all(), + $this->request->cookies->all(), + $this->request->files->all(), + $this->request->server->all(), + $content + ); + } + + $headerNames = [ + 'Authorization', + 'X-Experience-API-Version', + 'Content-Type', + 'Content-Length', + 'If-Match', + 'If-None-Match', + ]; + + foreach ($this->request->request as $key => $value) { + if (in_array($key, $headerNames, true)) { + $this->request->headers->set($key, $value); + } else { + $this->request->query->set($key, $value); + } + + $this->request->request->remove($key); + } + } +} diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php new file mode 100644 index 000000000..86dc548b0 --- /dev/null +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -0,0 +1,147 @@ +statementRepository = new StatementRepository( + $pluginEm->getRepository(StatementEntity::class) + ); + $this->serializer = Serializer::createSerializer(); + $this->serializerFactory = new SerializerFactory($this->serializer); + } + + public function get(): Response + { + $getStatementController = new StatementGetController( + $this->statementRepository, + $this->serializerFactory->createStatementSerializer(), + $this->serializerFactory->createStatementResultSerializer(), + new StatementsFilterFactory( + new ActorSerializer($this->serializer) + ) + ); + + return $getStatementController->getStatement($this->httpRequest); + } + + public function head(): Response + { + $headStatementController = new StatementHeadController( + $this->statementRepository, + $this->serializerFactory->createStatementSerializer(), + $this->serializerFactory->createStatementResultSerializer(), + new StatementsFilterFactory( + new ActorSerializer($this->serializer) + ) + ); + + return $headStatementController->getStatement($this->httpRequest); + } + + public function put(): Response + { + $statement = $this->serializerFactory + ->createStatementSerializer() + ->deserializeStatement( + $this->httpRequest->getContent() + ) + ; + + $putStatementController = new StatementPutController($this->statementRepository); + + $response = $putStatementController->putStatement($this->httpRequest, $statement); + + $this->saveLog( + [$this->httpRequest->query->get('statementId')] + ); + + return $response; + } + + public function post(): Response + { + $content = $this->httpRequest->getContent(); + + if (substr($content, 0, 1) !== '[') { + $content = "[$content]"; + } + + $statements = $this->serializerFactory + ->createStatementSerializer() + ->deserializeStatements($content) + ; + + $postStatementController = new StatementPostController($this->statementRepository); + + $response = $postStatementController->postStatements($this->httpRequest, $statements); + + $this->saveLog( + json_decode($response->getContent(), false) + ); + + return $response; + } + + /** + * @param array $statementsId + * + * @return void + */ + private function saveLog(array $statementsId) + { + foreach ($statementsId as $statementId) { + try { + $storedStatement = $this->statementRepository->findStatementById( + StatementId::fromString($statementId) + ); + + InternalLogUtil::saveStatementForInternalLog($storedStatement); + } catch (NotFoundException $e) { + } + } + } +} diff --git a/plugin/xapi/src/Lrs/Utils/InternalLogUtil.php b/plugin/xapi/src/Lrs/Utils/InternalLogUtil.php new file mode 100644 index 000000000..dc9a1cbcd --- /dev/null +++ b/plugin/xapi/src/Lrs/Utils/InternalLogUtil.php @@ -0,0 +1,115 @@ +getActor())) { + return; + } + + $statementObject = $statement->getObject(); + + if (!$statementObject instanceof Activity) { + return; + } + + $languageIso = api_get_language_isocode(); + $statementVerbString = XApiPlugin::extractVerbInLanguage($statement->getVerb()->getDisplay(), $languageIso); + + $internalLog = new InternalLog(); + $internalLog + ->setUser($user) + ->setVerb($statementVerbString) + ->setObjectId($statementObject->getId()->getValue()); + + if (null !== $statementId = $statement->getId()) { + $internalLog->setStatementId($statementId->getValue()); + } + + if (null !== $definition = $statementObject->getDefinition()) { + if (null !== $nameInLanguages = $definition->getName()) { + $internalLog->setActivityName( + XApiPlugin::extractVerbInLanguage($nameInLanguages, $languageIso) + ); + } + + if (null !== $descriptionInLanguage = $definition->getDescription()) { + $internalLog->setActivityDescription( + XApiPlugin::extractVerbInLanguage($descriptionInLanguage, $languageIso) + ); + } + } + + if (null !== $statementResult = $statement->getResult()) { + if (null !== $score = $statementResult->getScore()) { + $internalLog + ->setScoreScaled( + $score->getScaled() + ) + ->setScoreRaw( + $score->getRaw() + ) + ->setScoreMin( + $score->getMin() + ) + ->setScoreMax( + $score->getMax() + ); + } + } + + if (null !== $created = $statement->getCreated()) { + $internalLog->setCreatedAt($created); + } + + $em = Database::getManager(); + $em->persist($internalLog); + $em->flush(); + } + + private static function getUserFromActor(Actor $actor): ?object + { + if (!$actor instanceof Agent) { + return null; + } + + $actorIri = $actor->getInverseFunctionalIdentifier(); + + if (null === $actorIri) { + return null; + } + + $userRepo = UserManager::getRepository(); + + $user = null; + + if (null !== $mbox = $actorIri->getMbox()) { + if ((null !== $parts = explode(':', $mbox->getValue(), 2)) && !empty($parts[1])) { + $user = $userRepo->findOneBy(['email' => $parts[1]]); + } + } elseif (null !== $account = $actorIri->getAccount()) { + $chamiloIrl = IRL::fromString(api_get_path(WEB_PATH)); + + if ($account->getHomePage()->equals($chamiloIrl)) { + $user = $userRepo->findOneBy(['username' => $account->getName()]); + } + } + + return $user; + } +} diff --git a/plugin/xapi/src/Parser/Cmi5Parser.php b/plugin/xapi/src/Parser/Cmi5Parser.php new file mode 100644 index 000000000..0d7cc96c3 --- /dev/null +++ b/plugin/xapi/src/Parser/Cmi5Parser.php @@ -0,0 +1,178 @@ +filePath); + $xml = new Crawler($content); + + $courseNode = $xml->filterXPath('//courseStructure/course'); + + $toolLaunch = new ToolLaunch(); + $toolLaunch + ->setTitle( + current( + $this->getLanguageStrings( + $courseNode->filterXPath('//title') + ) + ) + ) + ->setDescription( + current( + $this->getLanguageStrings( + $courseNode->filterXPath('//description') + ) + ) + ) + ->setLaunchUrl('') + ->setActivityId($courseNode->attr('id')) + ->setActivityType('cmi5') + ->setAllowMultipleAttempts(false) + ->setCreatedAt(api_get_utc_datetime(null, false, true)) + ->setCourse($this->course) + ->setSession($this->session); + + $toc = $this->generateToC($xml); + + foreach ($toc as $cmi5Item) { + $toolLaunch->addItem($cmi5Item); + } + + return $toolLaunch; + } + + /** + * @return array + */ + private function getLanguageStrings(Crawler $node) + { + $map = []; + + foreach ($node->children() as $child) { + $key = $child->attributes['lang']->value; + $value = trim($child->textContent); + + $map[$key] = $value; + } + + return $map; + } + + /** + * @return array|\Chamilo\PluginBundle\Entity\XApi\Cmi5Item[] + */ + private function generateToC(Crawler $xml) + { + $blocksMap = []; + + /** @var array|Cmi5Item[] $items */ + $items = $xml + ->filterXPath('//*') + ->reduce( + function (Crawler $node, $i) { + return in_array($node->nodeName(), ['au', 'block']); + } + ) + ->each( + function (Crawler $node, $i) use (&$blocksMap) { + $attributes = ['id', 'activityType', 'launchMethod', 'moveOn', 'masteryScore']; + + list($id, $activityType, $launchMethod, $moveOn, $masteryMode) = $node->extract($attributes)[0]; + + $item = new Cmi5Item(); + $item + ->setIdentifier($id) + ->setType($node->nodeName()) + ->setTitle( + $this->getLanguageStrings( + $node->filterXPath('//title') + ) + ) + ->setDescription( + $this->getLanguageStrings( + $node->filterXPath('//description') + ) + ); + + if ('au' === $node->nodeName()) { + $launchParametersNode = $node->filterXPath('//launchParameters'); + $entitlementKeyNode = $node->filterXPath('//entitlementKey'); + $url + = $item + ->setUrl( + $this->parseLaunchUrl( + trim($node->filterXPath('//url')->text()) + ) + ) + ->setActivityType($activityType ?: null) + ->setLaunchMethod($launchMethod ?: null) + ->setMoveOn($moveOn ?: 'NotApplicable') + ->setMasteryScore((float) $masteryMode ?: null) + ->setLaunchParameters( + $launchParametersNode->count() > 0 ? trim($launchParametersNode->text()) : null + ) + ->setEntitlementKey( + $entitlementKeyNode->count() > 0 ? trim($entitlementKeyNode->text()) : null + ); + } + + $parentNode = $node->parents()->first(); + + if ('block' === $parentNode->nodeName()) { + $blocksMap[$i] = $parentNode->attr('id'); + } + + return $item; + } + ); + + foreach ($blocksMap as $itemPos => $parentIdentifier) { + foreach ($items as $item) { + if ($parentIdentifier === $item->getIdentifier()) { + $items[$itemPos]->setParent($item); + } + } + } + + return $items; + } + + /** + * @param string $url + * + * @return string + */ + private function parseLaunchUrl($url) + { + $urlInfo = parse_url($url); + + if (empty($urlInfo['scheme'])) { + $baseUrl = str_replace( + api_get_path(SYS_COURSE_PATH), + api_get_path(WEB_COURSE_PATH), + dirname($this->filePath) + ); + + return "$baseUrl/$url"; + } + + return $url; + } +} diff --git a/plugin/xapi/src/Parser/PackageParser.php b/plugin/xapi/src/Parser/PackageParser.php new file mode 100644 index 000000000..0454eca52 --- /dev/null +++ b/plugin/xapi/src/Parser/PackageParser.php @@ -0,0 +1,60 @@ +filePath = $filePath; + $this->course = $course; + $this->session = $session; + } + + /** + * @throws \Exception + * + * @return mixed + */ + public static function create(string $packageType, string $filePath, Course $course, Session $session = null) + { + switch ($packageType) { + case 'tincan': + return new TinCanParser($filePath, $course, $session); + case 'cmi5': + return new Cmi5Parser($filePath, $course, $session); + default: + throw new \Exception('Invalid package.'); + } + } + + abstract public function parse(): \Chamilo\PluginBundle\Entity\XApi\ToolLaunch; +} diff --git a/plugin/xapi/src/Parser/TinCanParser.php b/plugin/xapi/src/Parser/TinCanParser.php new file mode 100644 index 000000000..c8de58dc1 --- /dev/null +++ b/plugin/xapi/src/Parser/TinCanParser.php @@ -0,0 +1,69 @@ +filePath); + + $xml = new Crawler($content); + + $activityNode = $xml->filter('tincan activities activity')->first(); + $nodeName = $activityNode->filter('name'); + $nodeDescription = $activityNode->filter('description'); + $nodeLaunch = $activityNode->filter('launch'); + + $toolLaunch = new ToolLaunch(); + $toolLaunch + ->setCourse($this->course) + ->setSession($this->session) + ->setCreatedAt(api_get_utc_datetime(null, false, true)) + ->setActivityId($activityNode->attr('id')) + ->setActivityType($activityNode->attr('type')) + ->setLaunchUrl($this->parseLaunchUrl($nodeLaunch)); + + if ($nodeName) { + $toolLaunch->setTitle($nodeName->text()); + } + + if ($nodeDescription) { + $toolLaunch->setDescription($nodeDescription->text() ?: null); + } + + return $toolLaunch; + } + + private function parseLaunchUrl(Crawler $launchNode): string + { + $launchUrl = $launchNode->text(); + + $urlInfo = parse_url($launchUrl); + + if (empty($urlInfo['scheme'])) { + $baseUrl = str_replace( + api_get_path(SYS_COURSE_PATH), + api_get_path(WEB_COURSE_PATH), + dirname($this->filePath) + ); + + return "$baseUrl/$launchUrl"; + } + + return $launchUrl; + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/BaseActivity.php b/plugin/xapi/src/ToolExperience/Activity/BaseActivity.php new file mode 100644 index 000000000..c16902171 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/BaseActivity.php @@ -0,0 +1,49 @@ +getCourseLanguage()); + + $courseUrl = api_get_course_url( + $course->getCode(), + $session ? $session->getId() : 0 + ); + + return new Activity( + IRI::fromString($courseUrl), + new Definition( + LanguageMap::create([$languageIso => $course->getTitle()]), + null, + IRI::fromString('http://id.tincanapi.com/activitytype/lms/course') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/LearningPath.php b/plugin/xapi/src/ToolExperience/Activity/LearningPath.php new file mode 100644 index 000000000..1dad39950 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/LearningPath.php @@ -0,0 +1,53 @@ +lp = $lp; + } + + public function generate(): Activity + { + $lanIso = api_get_language_isocode(); + + $iri = $this->generateIri( + WEB_CODE_PATH, + 'lp/lp_controller.php', + [ + 'action' => 'view', + 'lp_id' => $this->lp->getId(), + 'isStudentView' => 'true', + ] + ); + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create([$lanIso => $this->lp->getName()]), + null, + IRI::fromString('http://adlnet.gov/expapi/activities/lesson') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/LearningPathItem.php b/plugin/xapi/src/ToolExperience/Activity/LearningPathItem.php new file mode 100644 index 000000000..6ac65fd65 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/LearningPathItem.php @@ -0,0 +1,54 @@ +lpItem = $lpItem; + } + + public function generate(): Activity + { + $langIso = api_get_language_isocode(); + + $iri = $this->generateIri( + WEB_CODE_PATH, + 'lp/lp_controller.php', + [ + 'action' => 'view', + 'lp_id' => $this->lpItem->getLpId(), + 'isStudentView' => 'true', + 'lp_item' => $this->lpItem->getId(), + ] + ); + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create([$langIso => $this->lpItem->getTitle()]), + null, + IRI::fromString('http://id.tincanapi.com/activitytype/resource') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/Portfolio.php b/plugin/xapi/src/ToolExperience/Activity/Portfolio.php new file mode 100644 index 000000000..7d391e176 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/Portfolio.php @@ -0,0 +1,50 @@ +owner = $owner; + } + + public function generate(): Activity + { + $langIso = api_get_language_isocode(); + + $iri = $this->generateIri( + WEB_CODE_PATH, + 'portfolio/index.php', + [ + 'action' => 'list', + 'user' => $this->owner->getId(), + ] + ); + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create( + [ + $langIso => sprintf( + get_lang('XUserPortfolioItems'), + $this->owner->getCompleteNameWithUsername() + ), + ] + ), + null, + IRI::fromString('http://id.tincanapi.com/activitytype/collection-simple') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/PortfolioCategory.php b/plugin/xapi/src/ToolExperience/Activity/PortfolioCategory.php new file mode 100644 index 000000000..5da9324b6 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/PortfolioCategory.php @@ -0,0 +1,58 @@ +category = $category; + } + + public function generate(): Activity + { + $iri = $this->generateIri( + WEB_PATH, + 'xapi/portfolio/', + [ + 'user' => $this->category->getUser()->getId(), + 'category' => $this->category->getId(), + ] + ); + + $langIso = api_get_language_isocode(); + + $categoryDescription = $this->category->getDescription(); + + $definitionDescription = $categoryDescription + ? LanguageMap::create([$langIso => $categoryDescription]) + : null; + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create([$langIso => $this->category->getTitle()]), + $definitionDescription, + IRI::fromString('http://id.tincanapi.com/activitytype/category') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/PortfolioComment.php b/plugin/xapi/src/ToolExperience/Activity/PortfolioComment.php new file mode 100644 index 000000000..9e7f91a41 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/PortfolioComment.php @@ -0,0 +1,50 @@ +comment = $comment; + } + + public function generate(): Activity + { + $iri = $this->generateIri( + WEB_CODE_PATH, + 'portfolio/index.php', + [ + 'action' => 'view', + 'id' => $this->comment->getItem()->getId(), + 'comment' => $this->comment->getId(), + ] + ); + + return new Activity( + IRI::fromString($iri), + new Definition( + null, + null, + IRI::fromString('http://activitystrea.ms/schema/1.0/comment') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/PortfolioItem.php b/plugin/xapi/src/ToolExperience/Activity/PortfolioItem.php new file mode 100644 index 000000000..8f995dd8a --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/PortfolioItem.php @@ -0,0 +1,49 @@ +item = $item; + } + + public function generate(): Activity + { + $langIso = api_get_language_isocode(); + + $iri = $this->generateIri( + WEB_CODE_PATH, + 'portfolio/index.php', + ['action' => 'view', 'id' => $this->item->getId()] + ); + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create([$langIso => $this->item->getTitle()]), + null, + IRI::fromString('http://activitystrea.ms/schema/1.0/article') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/Quiz.php b/plugin/xapi/src/ToolExperience/Activity/Quiz.php new file mode 100644 index 000000000..c4f9a5935 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/Quiz.php @@ -0,0 +1,57 @@ +quiz = $quiz; + } + + public function generate(): Activity + { + $langIso = api_get_language_isocode(); + + $iri = $this->generateIri( + WEB_CODE_PATH, + 'exercise/overview.php', + ['exerciseId' => $this->quiz->getId()] + ); + + $definitionDescription = null; + + if ($this->quiz->getDescription()) { + $definitionDescription = LanguageMap::create( + [$langIso => $this->quiz->getDescription()] + ); + } + + return new Activity( + IRI::fromString($iri), + new Definition( + LanguageMap::create([$langIso => $this->quiz->getTitle()]), + $definitionDescription, + IRI::fromString('http://adlnet.gov/expapi/activities/assessment') + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/QuizQuestion.php b/plugin/xapi/src/ToolExperience/Activity/QuizQuestion.php new file mode 100644 index 000000000..74c9a8110 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/QuizQuestion.php @@ -0,0 +1,170 @@ +question = $question; + } + + public function generate(): Activity + { + $iri = $this->generateIri( + WEB_CODE_PATH, + 'xapi/quiz/', + ['question' => $this->question->getId()] + ); + + return new Activity( + IRI::fromString($iri), + $this->generateActivityDefinitionFromQuestionType() + ); + } + + /** + * @return \Xabbuh\XApi\Model\Interaction\InteractionDefinition + */ + private function generateActivityDefinitionFromQuestionType() + { + $languageIso = api_get_language_isocode(); + $courseId = api_get_course_int_id(); + + $questionTitle = strip_tags($this->question->getQuestion()); + $questionTitle = trim($questionTitle); + $questionDescription = strip_tags($this->question->getDescription()); + $questionDescription = trim($questionDescription); + + $titleMap = LanguageMap::create([$languageIso => $questionTitle]); + $descriptionMap = $questionDescription ? LanguageMap::create([$languageIso => $questionDescription]) : null; + + $objAnswer = new Answer($this->question->getId(), $courseId); + $objAnswer->read(); + + $type = IRI::fromString('http://adlnet.gov/expapi/activities/question'); + + switch ($this->question->getType()) { + case MULTIPLE_ANSWER: + case UNIQUE_ANSWER: + case UNIQUE_ANSWER_IMAGE: + case READING_COMPREHENSION: + $choices = []; + $correctResponsesPattern = []; + + for ($i = 1; $i <= $objAnswer->nbrAnswers; $i++) { + $choices[] = new InteractionComponent( + $objAnswer->iid[$i], + LanguageMap::create([$languageIso => $objAnswer->selectAnswer($i)]) + ); + + if ($objAnswer->isCorrect($i)) { + $correctResponsesPattern[] = $objAnswer->iid[$i]; + } + } + + return new ChoiceInteractionDefinition( + $titleMap, + $descriptionMap, + $type, + null, + null, + [implode('[,]', $correctResponsesPattern)], + $choices + ); + case DRAGGABLE: + $choices = []; + + for ($i = 1; $i <= $objAnswer->nbrAnswers; $i++) { + if ((int) $objAnswer->correct[$i] > 0) { + $choices[] = new InteractionComponent( + $objAnswer->correct[$i], + LanguageMap::create([$languageIso => $objAnswer->answer[$i]]) + ); + } + } + + $correctResponsesPattern = array_slice($objAnswer->autoId, 0, $objAnswer->nbrAnswers / 2); + + return new SequencingInteractionDefinition( + $titleMap, + $descriptionMap, + $type, + null, + null, + [implode('[,]', $correctResponsesPattern)], + $choices + ); + case MATCHING: + case MATCHING_DRAGGABLE: + /** @var array|InteractionComponent[] $source */ + $source = []; + /** @var array|InteractionComponent[] $source */ + $target = []; + $correctResponsesPattern = []; + + for ($i = 1; $i <= $objAnswer->nbrAnswers; $i++) { + $interactionComponent = new InteractionComponent( + $objAnswer->selectAutoId($i), + LanguageMap::create([$languageIso => $objAnswer->selectAnswer($i)]) + ); + + if ((int) $objAnswer->correct[$i] > 0) { + $source[] = $interactionComponent; + + $correctResponsesPattern[] = $objAnswer->selectAutoId($i).'[.]'.$objAnswer->correct[$i]; + } else { + $target[] = $interactionComponent; + } + } + + return new MatchingInteractionDefinition( + $titleMap, + $descriptionMap, + $type, + null, + null, + [implode('[,]', $correctResponsesPattern)], + $source, + $target + ); + case FREE_ANSWER: + return new LongFillInInteractionDefinition($titleMap, $descriptionMap, $type); + case FILL_IN_BLANKS: + case HOT_SPOT: + case HOT_SPOT_DELINEATION: + case MULTIPLE_ANSWER_COMBINATION: + case UNIQUE_ANSWER_NO_OPTION: + case MULTIPLE_ANSWER_TRUE_FALSE: + case MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY: + case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE: + case GLOBAL_MULTIPLE_ANSWER: + case CALCULATED_ANSWER: + case ANNOTATION: + case ORAL_EXPRESSION: + default: + return new OtherInteractionDefinition($titleMap, $descriptionMap, $type); + } + } +} diff --git a/plugin/xapi/src/ToolExperience/Activity/Site.php b/plugin/xapi/src/ToolExperience/Activity/Site.php new file mode 100644 index 000000000..c074df33f --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Activity/Site.php @@ -0,0 +1,33 @@ + $platform]) + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Actor/BaseActor.php b/plugin/xapi/src/ToolExperience/Actor/BaseActor.php new file mode 100644 index 000000000..3ed8f00d3 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Actor/BaseActor.php @@ -0,0 +1,17 @@ +user = $user; + } + + public function generate(): Agent + { + return new Agent( + InverseFunctionalIdentifier::withMbox( + IRI::fromString('mailto:'.$this->user->getEmail()) + ), + $this->user->getCompleteName() + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/BaseStatement.php b/plugin/xapi/src/ToolExperience/Statement/BaseStatement.php new file mode 100644 index 000000000..370de27e6 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/BaseStatement.php @@ -0,0 +1,53 @@ +get(XApiPlugin::SETTING_UUID_NAMESPACE), + uniqid($type) + ); + + return StatementId::fromUuid($uuid); + } + + protected function generateContext(): Context + { + $platform = api_get_setting('Institution').' - '.api_get_setting('siteName'); + + $groupingActivities = []; + $groupingActivities[] = (new SiteActivity())->generate(); + + if (api_get_course_id()) { + $groupingActivities[] = (new CourseActivity())->generate(); + } + + return (new Context()) + ->withPlatform($platform) + ->withLanguage(api_get_language_isocode()) + ->withContextActivities( + new ContextActivities(null, $groupingActivities) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/LearningPathCompleted.php b/plugin/xapi/src/ToolExperience/Statement/LearningPathCompleted.php new file mode 100644 index 000000000..7629a9a8f --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/LearningPathCompleted.php @@ -0,0 +1,61 @@ +lpView = $lpView; + $this->lp = $lp; + } + + public function generate(): Statement + { + $user = api_get_user_entity($this->lpView->getUserId()); + $userActor = new UserActor($user); + $completedVerb = new Completed(); + $lpActivity = new LearningPathActivity($this->lp); + + return new Statement( + $this->generateStatementId('learning-path'), + $userActor->generate(), + $completedVerb->generate(), + $lpActivity->generate(), + new Result( + new Score(1, 100, 0, 100), + null, + true + ), + null, + api_get_utc_datetime(null, false, true), + null, + $this->generateContext() + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/LearningPathItemViewed.php b/plugin/xapi/src/ToolExperience/Statement/LearningPathItemViewed.php new file mode 100644 index 000000000..f2cc3942d --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/LearningPathItemViewed.php @@ -0,0 +1,79 @@ +lpItemView = $lpItemView; + $this->lpItem = $lpItem; + $this->lpView = $lpView; + } + + public function generate(): Statement + { + $user = api_get_user_entity($this->lpView->getUserId()); + $lp = Database::getManager()->find(CLp::class, $this->lpView->getLpId()); + + $userActor = new UserActor($user); + $viewedVerb = new ViewedVerb(); + $lpItemActivity = new LearningPathItemActivity($this->lpItem); + $lpActivity = new LearningPath($lp); + + $context = $this->generateContext(); + $contextActivities = $context + ->getContextActivities() + ->withAddedGroupingActivity($lpActivity->generate()); + + return new Statement( + $this->generateStatementId('learning-path-item'), + $userActor->generate(), + $viewedVerb->generate(), + $lpItemActivity->generate(), + new Result( + null, + null, + 'completed' === $this->lpItemView->getStatus(), + null, + 'PT'.$this->lpItemView->getTotalTime().'S' + ), + null, + api_get_utc_datetime(null, false, true), + null, + $context->withContextActivities($contextActivities) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioAttachmentsTrait.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioAttachmentsTrait.php new file mode 100644 index 000000000..4e66182f0 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioAttachmentsTrait.php @@ -0,0 +1,89 @@ + $portfolioAttachments + * + * @return array + */ + protected function generateAttachments(array $portfolioAttachments, User $user): array + { + if (empty($portfolioAttachments)) { + return []; + } + + $attachments = []; + + $userDirectory = UserManager::getUserPathById($user->getId(), 'system'); + $attachmentsDirectory = $userDirectory.'portfolio_attachments/'; + + $langIso = api_get_language_isocode(); + + $cidreq = api_get_cidreq(); + $baseUrl = api_get_path(WEB_CODE_PATH).'portfolio/index.php?'.($cidreq ? $cidreq.'&' : ''); + + foreach ($portfolioAttachments as $portfolioAttachment) { + $attachmentFilename = $attachmentsDirectory.$portfolioAttachment->getPath(); + + $display = LanguageMap::create( + ['und' => $portfolioAttachment->getFilename()] + ); + $description = null; + + if ($portfolioAttachment->getComment()) { + $description = LanguageMap::create( + [$langIso => $portfolioAttachment->getComment()] + ); + } + + $attachments[] = new Attachment( + IRI::fromString('http://id.tincanapi.com/attachment/supporting_media'), + mime_content_type($attachmentFilename), + $portfolioAttachment->getSize(), + hash_file('sha256', $attachmentFilename), + $display, + $description, + IRL::fromString( + $baseUrl.http_build_query(['action' => 'download', 'file' => $portfolioAttachment->getPath()]) + ) + ); + } + + return $attachments; + } + + protected function generateAttachmentsForItem(Portfolio $item): array + { + $itemAttachments = \Database::getManager() + ->getRepository(PortfolioAttachment::class) + ->findFromItem($item) + ; + + return $this->generateAttachments($itemAttachments, $item->getUser()); + } + + protected function generateAttachmentsForComment(PortfolioCommentEntity $comment): array + { + $commentAttachments = \Database::getManager() + ->getRepository(PortfolioAttachment::class) + ->findFromComment($this->comment) + ; + + return $this->generateAttachments($commentAttachments, $comment->getAuthor()); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioComment.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioComment.php new file mode 100644 index 000000000..f967d6a1c --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioComment.php @@ -0,0 +1,25 @@ +comment = $comment; + $this->item = $this->comment->getItem(); + $this->parentComment = $this->comment->getParent(); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentEdited.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentEdited.php new file mode 100644 index 000000000..5c2e2076e --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentEdited.php @@ -0,0 +1,39 @@ +comment); + $context = $this->generateContext(); + $attachements = $this->generateAttachmentsForComment($this->comment); + + return new Statement( + $this->generateStatementId('portfolio-comment'), + $actor->generate(), + $verb->generate(), + $object->generate(), + null, + null, + api_get_utc_datetime(null, false, true), + null, + $context, + $attachements + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentScored.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentScored.php new file mode 100644 index 000000000..2892173e4 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioCommentScored.php @@ -0,0 +1,47 @@ +comment->getScore(); + $scaled = $maxScore ? ($rawScore / $maxScore) : 0; + + $actor = new User($user); + $verb = new Scored(); + $object = new PortfolioCommentActivity($this->comment); + $context = $this->generateContext(); + $attachments = $this->generateAttachmentsForComment($this->comment); + $score = new Score($scaled, $rawScore, 0, $maxScore); + $result = new Result($score); + + return new Statement( + $this->generateStatementId('portfolio-comment'), + $actor->generate(), + $verb->generate(), + $object->generate(), + $result, + null, + api_get_utc_datetime(null, false, true), + null, + $context, + $attachments + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioDownloaded.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioDownloaded.php new file mode 100644 index 000000000..419f051ff --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioDownloaded.php @@ -0,0 +1,42 @@ +owner = $owner; + } + + public function generate(): Statement + { + $user = api_get_user_entity(api_get_user_id()); + + $actor = new UserActor($user); + $verb = new Downloaded(); + $object = new PortfolioActivity($this->owner); + $context = $this->generateContext(); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $actor->generate(), + $verb->generate(), + $object->generate(), + null, + null, + api_get_utc_datetime(null, false, true), + null, + $context + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItem.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItem.php new file mode 100644 index 000000000..bc8940eb9 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItem.php @@ -0,0 +1,41 @@ +item = $item; + } + + protected function generateContext(): Context + { + $context = parent::generateContext(); + + $category = $this->item->getCategory(); + + if ($category) { + $categoryActivity = new PortfolioCategory($category); + + $contextActivities = $context + ->getContextActivities() + ->withAddedCategoryActivity( + $categoryActivity->generate() + ) + ; + + $context = $context->withContextActivities($contextActivities); + } + + return $context; + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemCommented.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemCommented.php new file mode 100644 index 000000000..4f4e37cac --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemCommented.php @@ -0,0 +1,74 @@ +generateStatementId('portfolio-comment'); + + $userActor = new UserActor($this->comment->getAuthor()); + $statementResult = new Result(null, null, null, $this->comment->getContent()); + + $context = $this->generateContext(); + + $em = \Database::getManager(); + $commentAttachments = $em->getRepository(PortfolioAttachment::class)->findFromComment($this->comment); + + $attachments = $this->generateAttachments( + $commentAttachments, + $this->comment->getAuthor() + ); + + if ($this->parentComment) { + $repliedVerb = new RepliedVerb(); + + $itemActivity = new PortfolioItemActivity($this->item); + $parentCommentActivity = new PortfolioCommentActivity($this->parentComment); + + $contextActivities = $context + ->getContextActivities() + ->withAddedGroupingActivity($itemActivity->generate()); + + return new Statement( + $statementId, + $userActor->generate(), + $repliedVerb->generate(), + $parentCommentActivity->generate(), + $statementResult, + null, + $this->comment->getDate(), + null, + $context->withContextActivities($contextActivities), + $attachments + ); + } else { + $itemShared = new PortfolioItemShared($this->item); + + $commentedVerb = new CommentedVerb(); + + return $itemShared->generate() + ->withId($statementId) + ->withActor($userActor->generate()) + ->withVerb($commentedVerb->generate()) + ->withStored($this->comment->getDate()) + ->withResult($statementResult) + ->withContext($context) + ->withAttachments($attachments); + } + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemEdited.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemEdited.php new file mode 100644 index 000000000..f8ed7ae2e --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemEdited.php @@ -0,0 +1,39 @@ +item); + $context = $this->generateContext(); + $attachements = $this->generateAttachmentsForItem($this->item); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $actor->generate(), + $verb->generate(), + $object->generate(), + null, + null, + api_get_utc_datetime(null, false, true), + null, + $context, + $attachements + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemHighlighted.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemHighlighted.php new file mode 100644 index 000000000..b0d6a33bf --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemHighlighted.php @@ -0,0 +1,39 @@ +item); + $context = $this->generateContext(); + $attachments = $this->generateAttachmentsForItem($this->item); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $actor->generate(), + $verb->generate(), + $object->generate(), + null, + null, + api_get_utc_datetime(null, false, true), + null, + $context, + $attachments + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemScored.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemScored.php new file mode 100644 index 000000000..73977a0d7 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemScored.php @@ -0,0 +1,47 @@ +item->getScore(); + $scaled = $maxScore ? ($rawScore / $maxScore) : 0; + + $actor = new User($user); + $verb = new Scored(); + $object = new PortfolioItemActivity($this->item); + $context = $this->generateContext(); + $attachments = $this->generateAttachmentsForItem($this->item); + $score = new Score($scaled, $rawScore, 0, $maxScore); + $result = new Result($score); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $actor->generate(), + $verb->generate(), + $object->generate(), + $result, + null, + api_get_utc_datetime(null, false, true), + null, + $context, + $attachments + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemShared.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemShared.php new file mode 100644 index 000000000..ee44f0473 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemShared.php @@ -0,0 +1,47 @@ +item->getUser(); + + $userActor = new User($itemAuthor); + $sharedVerb = new Shared(); + $itemActivity = new PortfolioItem($this->item); + + $context = $this->generateContext(); + + $attachments = $this->generateAttachmentsForItem($this->item); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $userActor->generate(), + $sharedVerb->generate(), + $itemActivity->generate(), + null, + null, + $this->item->getCreationDate(), + null, + $context, + $attachments + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/PortfolioItemViewed.php b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemViewed.php new file mode 100644 index 000000000..3612ab088 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/PortfolioItemViewed.php @@ -0,0 +1,40 @@ +item); + $context = $this->generateContext(); + $attachments = $this->generateAttachmentsForItem($this->item); + + return new Statement( + $this->generateStatementId('portfolio-item'), + $actor->generate(), + $verb->generate(), + $object->generate(), + null, + null, + $this->item->getCreationDate(), + null, + $context, + $attachments + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/QuizCompleted.php b/plugin/xapi/src/ToolExperience/Statement/QuizCompleted.php new file mode 100644 index 000000000..2a4e9ba51 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/QuizCompleted.php @@ -0,0 +1,70 @@ +exe = $exe; + $this->quiz = $quiz; + } + + public function generate(): Statement + { + $user = api_get_user_entity($this->exe->getExeUserId()); + + $userActor = new UserActor($user); + $completedVerb = new CompletedVerb(); + $quizActivity = new QuizActivity($this->quiz); + + $rawResult = $this->exe->getExeResult(); + $maxResult = $this->exe->getExeWeighting(); + $scaledResult = $rawResult / $maxResult; + + $duration = $this->exe->getExeDuration(); + + return new Statement( + $this->generateStatementId('exercise'), + $userActor->generate(), + $completedVerb->generate(), + $quizActivity->generate(), + new Result( + new Score($scaledResult, $rawResult, 0, $maxResult), + null, + true, + null, + $duration ? "PT{$duration}S" : null + ), + null, + $this->exe->getExeDate(), + null, + $this->generateContext() + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Statement/QuizQuestionAnswered.php b/plugin/xapi/src/ToolExperience/Statement/QuizQuestionAnswered.php new file mode 100644 index 000000000..f891e4ce4 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Statement/QuizQuestionAnswered.php @@ -0,0 +1,79 @@ +attempt = $attempt; + $this->question = $question; + $this->quiz = $quiz; + } + + public function generate(): Statement + { + $user = api_get_user_entity($this->attempt->getUserId()); + + $userActor = new UserActor($user); + $answeredVerb = new AnsweredVerb(); + $questionActivity = new QuizQuestionActivity($this->question); + $quizActivity = new QuizActivity($this->quiz); + + $rawResult = $this->attempt->getMarks(); + $maxResult = $this->question->getPonderation(); + $scaledResult = $maxResult ? ($rawResult / $maxResult) : 0; + + $context = $this->generateContext(); + $contextActivities = $context + ->getContextActivities() + ->withAddedGroupingActivity($quizActivity->generate()); + + return new Statement( + $this->generateStatementId('exercise-question'), + $userActor->generate(), + $answeredVerb->generate(), + $questionActivity->generate(), + new Result( + new Score($scaledResult, $rawResult, null, $maxResult), + $rawResult > 0, + true + ), + null, + $this->attempt->getTms(), + null, + $context->withContextActivities($contextActivities) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Verb/Answered.php b/plugin/xapi/src/ToolExperience/Verb/Answered.php new file mode 100644 index 000000000..4007892f3 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Verb/Answered.php @@ -0,0 +1,21 @@ +iri = $iri; + $this->display = $display; + } + + public function generate(): Verb + { + $langIso = api_get_language_isocode(); + + return new Verb( + IRI::fromString($this->iri), + LanguageMap::create( + [ + $langIso => get_lang($this->display), + ] + ) + ); + } +} diff --git a/plugin/xapi/src/ToolExperience/Verb/Commented.php b/plugin/xapi/src/ToolExperience/Verb/Commented.php new file mode 100644 index 000000000..34865a6b3 --- /dev/null +++ b/plugin/xapi/src/ToolExperience/Verb/Commented.php @@ -0,0 +1,21 @@ +', + ]; + $settings = [ + self::SETTING_UUID_NAMESPACE => 'text', + + self::SETTING_LRS_URL => 'text', + self::SETTING_LRS_AUTH_USERNAME => 'text', + self::SETTING_LRS_AUTH_PASSWORD => 'text', + + self::SETTING_CRON_LRS_URL => 'text', + self::SETTING_CRON_LRS_AUTH_USERNAME => 'text', + self::SETTING_CRON_LRS_AUTH_PASSWORD => 'text', + + self::SETTING_LRS_LP_ITEM_ACTIVE => 'boolean', + self::SETTING_LRS_LP_ACTIVE => 'boolean', + self::SETTING_LRS_QUIZ_ACTIVE => 'boolean', + self::SETTING_LRS_QUIZ_QUESTION_ACTIVE => 'boolean', + self::SETTING_LRS_PORTFOLIO_ACTIVE => 'boolean', + ]; + + parent::__construct( + $version, + implode(', ', $author), + $settings + ); + } + + /** + * @return \XApiPlugin + */ + public static function create() + { + static $result = null; + + return $result ? $result : $result = new self(); + } + + /** + * Process to install plugin. + */ + public function install() + { + $em = Database::getManager(); + + $tablesExists = $em->getConnection()->getSchemaManager()->tablesExist( + [ + 'xapi_shared_statement', + 'xapi_tool_launch', + 'xapi_lrs_auth', + 'xapi_cmi5_item', + 'xapi_activity_state', + 'xapi_activity_profile', + 'xapi_internal_log', + + 'xapi_attachment', + 'xapi_object', + 'xapi_result', + 'xapi_verb', + 'xapi_extensions', + 'xapi_context', + 'xapi_actor', + 'xapi_statement', + ] + ); + + if ($tablesExists) { + return; + } + + $this->installPluginDbTables(); + $this->installInitialConfig(); + $this->addCourseTools(); + $this->installHook(); + } + + /** + * Process to uninstall plugin. + */ + public function uninstall() + { + $this->uninstallHook(); + $this->uninstallPluginDbTables(); + $this->deleteCourseTools(); + } + + /** + * {@inheritdoc} + */ + public function uninstallHook() + { + $learningPathItemViewedHook = XApiLearningPathItemViewedHookObserver::create(); + $learningPathEndHook = XApiLearningPathEndHookObserver::create(); + $quizQuestionAnsweredHook = XApiQuizQuestionAnsweredHookObserver::create(); + $quizEndHook = XApiQuizEndHookObserver::create(); + $createCourseHook = XApiCreateCourseHookObserver::create(); + $portfolioItemAddedHook = XApiPortfolioItemAddedHookObserver::create(); + $portfolioItemCommentedHook = XApiPortfolioItemCommentedHookObserver::create(); + $portfolioItemHighlightedHook = XApiPortfolioItemHighlightedHookObserver::create(); + $portfolioDownloaded = XApiPortfolioDownloadedHookObserver::create(); + $portfolioItemScoredHook = XApiPortfolioItemScoredHookObserver::create(); + $portfolioCommentedScoredHook = XApiPortfolioCommentScoredHookObserver::create(); + $portfolioItemEditedHook = XApiPortfolioItemEditedHookObserver::create(); + $portfolioCommentEditedHook = XApiPortfolioCommentEditedHookObserver::create(); + + HookLearningPathItemViewed::create()->detach($learningPathItemViewedHook); + HookLearningPathEnd::create()->detach($learningPathEndHook); + HookQuizQuestionAnswered::create()->detach($quizQuestionAnsweredHook); + HookQuizEnd::create()->detach($quizEndHook); + HookCreateCourse::create()->detach($createCourseHook); + HookPortfolioItemAdded::create()->detach($portfolioItemAddedHook); + HookPortfolioItemCommented::create()->detach($portfolioItemCommentedHook); + HookPortfolioItemHighlighted::create()->detach($portfolioItemHighlightedHook); + HookPortfolioDownloaded::create()->detach($portfolioDownloaded); + HookPortfolioItemScored::create()->detach($portfolioItemScoredHook); + HookPortfolioCommentScored::create()->detach($portfolioCommentedScoredHook); + HookPortfolioItemEdited::create()->detach($portfolioItemEditedHook); + HookPortfolioCommentEdited::create()->detach($portfolioCommentEditedHook); + + return 1; + } + + public function uninstallPluginDbTables() + { + $em = Database::getManager(); + $pluginEm = self::getEntityManager(); + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema( + [ + $em->getClassMetadata(ActivityProfile::class), + $em->getClassMetadata(ActivityState::class), + $em->getClassMetadata(SharedStatement::class), + $em->getClassMetadata(ToolLaunch::class), + $em->getClassMetadata(LrsAuth::class), + $em->getClassMetadata(Cmi5Item::class), + $em->getClassMetadata(InternalLog::class), + ] + ); + + $pluginSchemaTool = new SchemaTool($pluginEm); + $pluginSchemaTool->dropSchema( + [ + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Attachment::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\StatementObject::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Result::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Verb::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Extensions::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Context::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Actor::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Statement::class), + ] + ); + } + + /** + * @param string|null $lrsUrl + * @param string|null $lrsAuthUsername + * @param string|null $lrsAuthPassword + * + * @return \Xabbuh\XApi\Client\Api\StateApiClientInterface + */ + public function getXApiStateClient($lrsUrl = null, $lrsAuthUsername = null, $lrsAuthPassword = null) + { + return $this + ->createXApiClient($lrsUrl, $lrsAuthUsername, $lrsAuthPassword) + ->getStateApiClient(); + } + + public function getXApiStatementClient(): StatementsApiClientInterface + { + return $this->createXApiClient()->getStatementsApiClient(); + } + + public function getXapiStatementCronClient(): StatementsApiClientInterface + { + $lrsUrl = $this->get(self::SETTING_CRON_LRS_URL); + $lrsUsername = $this->get(self::SETTING_CRON_LRS_AUTH_USERNAME); + $lrsPassword = $this->get(self::SETTING_CRON_LRS_AUTH_PASSWORD); + + return $this + ->createXApiClient( + empty($lrsUrl) ? null : $lrsUrl, + empty($lrsUsername) ? null : $lrsUsername, + empty($lrsPassword) ? null : $lrsPassword + ) + ->getStatementsApiClient(); + } + + /** + * Perform actions after save the plugin configuration. + * + * @return \XApiPlugin + */ + public function performActionsAfterConfigure() + { + $learningPathItemViewedHook = XApiLearningPathItemViewedHookObserver::create(); + $learningPathEndHook = XApiLearningPathEndHookObserver::create(); + $quizQuestionAnsweredHook = XApiQuizQuestionAnsweredHookObserver::create(); + $quizEndHook = XApiQuizEndHookObserver::create(); + $portfolioItemAddedHook = XApiPortfolioItemAddedHookObserver::create(); + $portfolioItemCommentedHook = XApiPortfolioItemCommentedHookObserver::create(); + $portfolioItemViewedHook = XApiPortfolioItemViewedHookObserver::create(); + $portfolioItemHighlightedHook = XApiPortfolioItemHighlightedHookObserver::create(); + $portfolioDownloadedHook = XApiPortfolioDownloadedHookObserver::create(); + $portfolioItemScoredHook = XApiPortfolioItemScoredHookObserver::create(); + $portfolioCommentScoredHook = XApiPortfolioCommentScoredHookObserver::create(); + $portfolioItemEditedHook = XApiPortfolioItemEditedHookObserver::create(); + $portfolioCommentEditedHook = XApiPortfolioCommentEditedHookObserver::create(); + + $learningPathItemViewedEvent = HookLearningPathItemViewed::create(); + $learningPathEndEvent = HookLearningPathEnd::create(); + $quizQuestionAnsweredEvent = HookQuizQuestionAnswered::create(); + $quizEndEvent = HookQuizEnd::create(); + $portfolioItemAddedEvent = HookPortfolioItemAdded::create(); + $portfolioItemCommentedEvent = HookPortfolioItemCommented::create(); + $portfolioItemViewedEvent = HookPortfolioItemViewed::create(); + $portfolioItemHighlightedEvent = HookPortfolioItemHighlighted::create(); + $portfolioDownloadedEvent = HookPortfolioDownloaded::create(); + $portfolioItemScoredEvent = HookPortfolioItemScored::create(); + $portfolioCommentScoredEvent = HookPortfolioCommentScored::create(); + $portfolioItemEditedEvent = HookPortfolioItemEdited::create(); + $portfolioCommentEditedEvent = HookPortfolioCommentEdited::create(); + + if ('true' === $this->get(self::SETTING_LRS_LP_ITEM_ACTIVE)) { + $learningPathItemViewedEvent->attach($learningPathItemViewedHook); + } else { + $learningPathItemViewedEvent->detach($learningPathItemViewedHook); + } + + if ('true' === $this->get(self::SETTING_LRS_LP_ACTIVE)) { + $learningPathEndEvent->attach($learningPathEndHook); + } else { + $learningPathEndEvent->detach($learningPathEndHook); + } + + if ('true' === $this->get(self::SETTING_LRS_QUIZ_ACTIVE)) { + $quizQuestionAnsweredEvent->attach($quizQuestionAnsweredHook); + } else { + $quizQuestionAnsweredEvent->detach($quizQuestionAnsweredHook); + } + + if ('true' === $this->get(self::SETTING_LRS_QUIZ_QUESTION_ACTIVE)) { + $quizEndEvent->attach($quizEndHook); + } else { + $quizEndEvent->detach($quizEndHook); + } + + if ('true' === $this->get(self::SETTING_LRS_PORTFOLIO_ACTIVE)) { + $portfolioItemAddedEvent->attach($portfolioItemAddedHook); + $portfolioItemCommentedEvent->attach($portfolioItemCommentedHook); + $portfolioItemViewedEvent->attach($portfolioItemViewedHook); + $portfolioItemHighlightedEvent->attach($portfolioItemHighlightedHook); + $portfolioDownloadedEvent->attach($portfolioDownloadedHook); + $portfolioItemScoredEvent->attach($portfolioItemScoredHook); + $portfolioCommentScoredEvent->attach($portfolioCommentScoredHook); + $portfolioItemEditedEvent->attach($portfolioItemEditedHook); + $portfolioCommentEditedEvent->attach($portfolioCommentEditedHook); + } else { + $portfolioItemAddedEvent->detach($portfolioItemAddedHook); + $portfolioItemCommentedEvent->detach($portfolioItemCommentedHook); + $portfolioItemViewedEvent->detach($portfolioItemViewedHook); + $portfolioItemHighlightedEvent->detach($portfolioItemHighlightedHook); + $portfolioDownloadedEvent->detach($portfolioDownloadedHook); + $portfolioItemScoredEvent->detach($portfolioItemScoredHook); + $portfolioCommentScoredEvent->detach($portfolioCommentScoredHook); + $portfolioItemEditedEvent->detach($portfolioItemEditedHook); + $portfolioCommentEditedEvent->detach($portfolioCommentEditedHook); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function installHook() + { + $createCourseHook = XApiCreateCourseHookObserver::create(); + + HookCreateCourse::create()->attach($createCourseHook); + } + + /** + * @param string $variable + * + * @return array + */ + public function getLangMap($variable) + { + $platformLanguage = api_get_setting('platformLanguage'); + $platformLanguageIso = api_get_language_isocode($platformLanguage); + + $map = []; + $map[$platformLanguageIso] = $this->getLangFromFile($variable, $platformLanguage); + + try { + $interfaceLanguage = api_get_interface_language(); + } catch (Exception $e) { + return $map; + } + + if (!empty($interfaceLanguage) && $platformLanguage !== $interfaceLanguage) { + $interfaceLanguageIso = api_get_language_isocode($interfaceLanguage); + + $map[$interfaceLanguageIso] = $this->getLangFromFile($variable, $interfaceLanguage); + } + + return $map; + } + + /** + * @param string $value + * @param string $type + * + * @return \Xabbuh\XApi\Model\IRI + */ + public function generateIri($value, $type) + { + return IRI::fromString( + api_get_path(WEB_PATH)."xapi/$type/$value" + ); + } + + /** + * @param int $courseId + */ + public function addCourseToolForTinCan($courseId) + { + // The $link param is set to "../plugin" as a hack to link correctly to the plugin URL in course tool. + // Otherwise, the link en the course tool will link to "/main/" URL. + $this->createLinkToCourseTool( + $this->get_lang('ToolTinCan'), + $courseId, + 'sessions_category.png', + '../plugin/xapi/start.php', + 0, + 'authoring' + ); + } + + /** + * @param string $language + * + * @return mixed|string + */ + public static function extractVerbInLanguage(Xabbuh\XApi\Model\LanguageMap $languageMap, $language) + { + $iso = self::findLanguageIso($languageMap->languageTags(), $language); + + $text = current($languageMap); + + if (isset($languageMap[$iso])) { + $text = trim($languageMap[$iso]); + } elseif (isset($languageMap['und'])) { + $text = $languageMap['und']; + } + + return $text; + } + + /** + * @param string $needle + * + * @return string + */ + public static function findLanguageIso(array $haystack, $needle) + { + if (in_array($needle, $haystack)) { + return $needle; + } + + foreach ($haystack as $language) { + if (strpos($language, $needle) === 0) { + return $language; + } + } + + return $haystack[0]; + } + + public function generateLaunchUrl( + $type, + $launchUrl, + $activityId, + Agent $actor, + $attemptId, + $customLrsUrl = null, + $customLrsUsername = null, + $customLrsPassword = null, + $viewSessionId = null + ) { + $lrsUrl = $customLrsUrl ?: $this->get(self::SETTING_LRS_URL); + $lrsAuthUsername = $customLrsUsername ?: $this->get(self::SETTING_LRS_AUTH_USERNAME); + $lrsAuthPassword = $customLrsPassword ?: $this->get(self::SETTING_LRS_AUTH_PASSWORD); + + $queryData = [ + 'endpoint' => trim($lrsUrl, "/ \t\n\r\0\x0B"), + 'actor' => Serializer::createSerializer()->serialize($actor, 'json'), + 'registration' => $attemptId, + ]; + + if ('tincan' === $type) { + $queryData['auth'] = 'Basic '.base64_encode(trim($lrsAuthUsername).':'.trim($lrsAuthPassword)); + $queryData['activity_id'] = $activityId; + } elseif ('cmi5' === $type) { + $queryData['fetch'] = api_get_path(WEB_PLUGIN_PATH).'xapi/cmi5/token.php?session='.$viewSessionId; + $queryData['activityId'] = $activityId; + } + + return $launchUrl.'?'.http_build_query($queryData, null, '&', PHP_QUERY_RFC3986); + } + + /** + * @return \Doctrine\ORM\EntityManager|null + */ + public static function getEntityManager() + { + $em = Database::getManager(); + + $prefixes = [ + __DIR__.'/../php-xapi/repository-doctrine-orm/metadata' => 'XApi\Repository\Doctrine\Mapping', + ]; + + $driver = new SimplifiedXmlDriver($prefixes); + $driver->setGlobalBasename('global'); + + $config = Database::getDoctrineConfig(api_get_configuration_value('root_sys')); + $config->setMetadataDriverImpl($driver); + + try { + return EntityManager::create($em->getConnection()->getParams(), $config); + } catch (ORMException $e) { + api_not_allowed(true, $e->getMessage()); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getAdminUrl() + { + $webPath = api_get_path(WEB_PLUGIN_PATH).$this->get_name(); + + return "$webPath/admin.php"; + } + + public function getLpResourceBlock(int $lpId) + { + $cidReq = api_get_cidreq(true, true, 'lp'); + $webPath = api_get_path(WEB_PLUGIN_PATH).'xapi/'; + $course = api_get_course_entity(); + $session = api_get_session_entity(); + + $tools = Database::getManager() + ->getRepository(ToolLaunch::class) + ->findByCourseAndSession($course, $session); + + $importIcon = Display::return_icon('import_scorm.png'); + $moveIcon = Display::url( + Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY), + '#', + ['class' => 'moved'] + ); + + $return = '
    • ' + .$importIcon + .Display::url( + get_lang('Import'), + $webPath."tool_import.php?$cidReq&".http_build_query(['lp_id' => $lpId]) + ) + .'
    • '; + + /** @var ToolLaunch $tool */ + foreach ($tools as $tool) { + $toolAnchor = Display::url( + Security::remove_XSS($tool->getTitle()), + api_get_self()."?$cidReq&" + .http_build_query( + ['action' => 'add_item', 'type' => TOOL_XAPI, 'file' => $tool->getId(), 'lp_id' => $lpId] + ), + ['class' => 'moved'] + ); + + $return .= Display::tag( + 'li', + $moveIcon.$importIcon.$toolAnchor, + [ + 'class' => 'lp_resource_element', + 'data_id' => $tool->getId(), + 'data_type' => TOOL_XAPI, + 'title' => $tool->getTitle(), + ] + ); + } + + $return .= '
    '; + + return $return; + } + + /** + * @throws \Doctrine\ORM\Tools\ToolsException + */ + private function installPluginDbTables() + { + $em = Database::getManager(); + $pluginEm = self::getEntityManager(); + + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema( + [ + $em->getClassMetadata(SharedStatement::class), + $em->getClassMetadata(ToolLaunch::class), + $em->getClassMetadata(LrsAuth::class), + $em->getClassMetadata(Cmi5Item::class), + $em->getClassMetadata(ActivityState::class), + $em->getClassMetadata(ActivityProfile::class), + $em->getClassMetadata(InternalLog::class), + ] + ); + + $pluginSchemaTool = new SchemaTool($pluginEm); + $pluginSchemaTool->createSchema( + [ + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Attachment::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\StatementObject::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Result::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Verb::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Extensions::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Context::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Actor::class), + $pluginEm->getClassMetadata(\XApi\Repository\Doctrine\Mapping\Statement::class), + ] + ); + } + + /** + * @throws \Exception + */ + private function installInitialConfig() + { + $uuidNamespace = Uuid::uuid1(); + + $pluginName = $this->get_name(); + $urlId = api_get_current_access_url_id(); + + api_add_setting( + $uuidNamespace, + $pluginName.'_'.self::SETTING_UUID_NAMESPACE, + $pluginName, + 'setting', + 'Plugins', + $pluginName, + '', + '', + '', + $urlId, + 1 + ); + + api_add_setting( + api_get_path(WEB_PATH).'plugin/xapi/lrs.php', + $pluginName.'_'.self::SETTING_LRS_URL, + $pluginName, + 'setting', + 'Plugins', + $pluginName, + '', + '', + '', + $urlId, + 1 + ); + } + + /** + * @param string|null $lrsUrl + * @param string|null $lrsAuthUsername + * @param string|null $lrsAuthPassword + * + * @return \Xabbuh\XApi\Client\XApiClientInterface + */ + private function createXApiClient($lrsUrl = null, $lrsAuthUsername = null, $lrsAuthPassword = null) + { + $baseUrl = $lrsUrl ?: $this->get(self::SETTING_LRS_URL); + $lrsAuthUsername = $lrsAuthUsername ?: $this->get(self::SETTING_LRS_AUTH_USERNAME); + $lrsAuthPassword = $lrsAuthPassword ?: $this->get(self::SETTING_LRS_AUTH_PASSWORD); + + $clientBuilder = new XApiClientBuilder(); + $clientBuilder + ->setHttpClient(Client::createWithConfig([RequestOptions::VERIFY => false])) + ->setRequestFactory(new GuzzleMessageFactory()) + ->setBaseUrl(trim($baseUrl, "/ \t\n\r\0\x0B")) + ->setAuth(trim($lrsAuthUsername), trim($lrsAuthPassword)); + + return $clientBuilder->build(); + } + + private function addCourseTools() + { + $courses = Database::getManager() + ->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c') + ->getResult(); + + foreach ($courses as $course) { + $this->addCourseToolForTinCan($course['id']); + } + } + + private function deleteCourseTools() + { + Database::getManager() + ->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link') + ->execute(['category' => 'authoring', 'link' => '../plugin/xapi/start.php%']); + } +} diff --git a/plugin/xapi/start.php b/plugin/xapi/start.php new file mode 100644 index 000000000..f3d305a4b --- /dev/null +++ b/plugin/xapi/start.php @@ -0,0 +1,151 @@ +getRepository(ToolLaunch::class); + +$course = api_get_course_entity(); +$session = api_get_session_entity(); +$userInfo = api_get_user_info(); + +$cidReq = api_get_cidreq(); + +$table = new SortableTable( + 'tbl_xapi', + function () use ($em, $course, $session, $isAllowedToEdit) { + return $em->getRepository(ToolLaunch::class) + ->countByCourseAndSession($course, $session, !$isAllowedToEdit); + }, + function ($start, $limit, $orderBy, $orderDir) use ($toolLaunchRepo, $course, $session, $isAllowedToEdit) { + $tools = $toolLaunchRepo->findByCourseAndSession($course, $session, ['title' => $orderDir], $limit, $start); + + $data = []; + + /** @var ToolLaunch $toolLaunch */ + foreach ($tools as $toolLaunch) { + $wasAddedInLp = $toolLaunchRepo->wasAddedInLp($toolLaunch); + + if ($wasAddedInLp && !$isAllowedToEdit) { + continue; + } + + $datum = []; + $datum[] = [ + $toolLaunch->getId(), + $toolLaunch->getTitle(), + $toolLaunch->getDescription(), + $toolLaunch->getActivityType(), + $wasAddedInLp, + ]; + + if ($isAllowedToEdit) { + $datum[] = $toolLaunch->getId(); + } + + $data[] = $datum; + } + + return $data; + }, + 0 +); +$table->set_header(0, $plugin->get_lang('ActivityTitle'), true); +$table->set_column_filter( + 0, + function (array $toolInfo) use ($cidReq, $session, $userInfo, $plugin) { + list($id, $title, $description, $activityType, $wasAddedInLp) = $toolInfo; + + $sessionStar = api_get_session_image( + $session ? $session->getId() : 0, + $userInfo['status'] + ); + + $data = Display::url( + $title.$sessionStar, + ('cmi5' === $activityType ? 'cmi5/view.php' : 'tincan/view.php')."?id=$id&$cidReq", + ['class' => 'show'] + ); + + if ($description) { + $data .= PHP_EOL.Display::tag('small', $description, ['class' => 'text-muted']); + } + + if ($wasAddedInLp) { + $data .= Display::div( + $plugin->get_lang('ActivityAddedToLPCannotBeAccessed'), + ['class' => 'lp_content_type_label'] + ); + } + + return $data; + } +); + +if ($isAllowedToEdit) { + $thAttributes = ['class' => 'text-right', 'style' => 'width: 100px;']; + + $table->set_header(1, get_lang('Actions'), false, $thAttributes, $thAttributes); + $table->set_column_filter( + 1, + function ($id) use ($cidReq, $isAllowedToEdit) { + $actions = []; + + if ($isAllowedToEdit) { + $actions[] = Display::url( + Display::return_icon('statistics.png', get_lang('Reporting')), + "tincan/stats.php?$cidReq&id=$id" + ); + $actions[] = Display::url( + Display::return_icon('edit.png', get_lang('Edit')), + "tool_edit.php?$cidReq&edit=$id" + ); + $actions[] = Display::url( + Display::return_icon('delete.png', get_lang('Delete')), + "tool_delete.php?$cidReq&delete=$id" + ); + } + + return implode(PHP_EOL, $actions); + } + ); +} + +$pageTitle = $plugin->get_lang('ToolTinCan'); +$pageContent = Display::return_message($plugin->get_lang('NoActivities'), 'info'); + +if ($table->get_total_number_of_items() > 0) { + $pageContent = $table->return_table(); +} + +$view = new Template($pageTitle); +$view->assign('header', $pageTitle); + +if ($isAllowedToEdit) { + $actions = Display::url( + Display::return_icon('import_scorm.png', get_lang('Import'), [], ICON_SIZE_MEDIUM), + "tool_import.php?$cidReq" + ); + + $view->assign( + 'actions', + Display::toolbarAction( + 'xapi_actions', + [$actions] + ) + ); +} + +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/tincan/launch.php b/plugin/xapi/tincan/launch.php new file mode 100644 index 000000000..b61eee3bd --- /dev/null +++ b/plugin/xapi/tincan/launch.php @@ -0,0 +1,142 @@ +request->get('attempt_id'); +$toolLaunch = $em->find( + ToolLaunch::class, + $request->request->getInt('id') +); + +if (empty($attemptId) + || null === $toolLaunch + || $toolLaunch->getCourse()->getId() !== api_get_course_entity()->getId() +) { + api_not_allowed(true); +} + +$plugin = XApiPlugin::create(); + +$activity = new Activity( + IRI::fromString($toolLaunch->getActivityId()) +); +$actor = new Agent( + InverseFunctionalIdentifier::withMbox( + IRI::fromString('mailto:'.$user->getEmail()) + ), + $user->getCompleteName() +); +$state = new State( + $activity, + $actor, + $plugin->generateIri('tool-'.$toolLaunch->getId(), 'state')->getValue() +); + +$nowDate = api_get_utc_datetime(null, false, true)->format('c'); + +try { + $stateDocument = $plugin + ->getXApiStateClient( + $toolLaunch->getLrsUrl(), + $toolLaunch->getLrsAuthUsername(), + $toolLaunch->getLrsAuthPassword() + ) + ->getDocument($state); + + $data = $stateDocument->getData()->getData(); + + if ($stateDocument->offsetExists($attemptId)) { + $data[$attemptId][XApiPlugin::STATE_LAST_LAUNCH] = $nowDate; + } else { + $data[$attemptId] = [ + XApiPlugin::STATE_FIRST_LAUNCH => $nowDate, + XApiPlugin::STATE_LAST_LAUNCH => $nowDate, + ]; + } + + uasort( + $data, + function ($attemptA, $attemptB) { + $timeA = strtotime($attemptA[XApiPlugin::STATE_LAST_LAUNCH]); + $timeB = strtotime($attemptB[XApiPlugin::STATE_LAST_LAUNCH]); + + return $timeB - $timeA; + } + ); + + $documentData = new DocumentData($data); +} catch (NotFoundException $notFoundException) { + $documentData = new DocumentData( + [ + $attemptId => [ + XApiPlugin::STATE_FIRST_LAUNCH => $nowDate, + XApiPlugin::STATE_LAST_LAUNCH => $nowDate, + ], + ] + ); +} catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + + header('Location: '.api_get_course_url()); + exit; +} + +try { + $plugin + ->getXApiStateClient() + ->createOrReplaceDocument( + new StateDocument($state, $documentData) + ); +} catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + + header('Location: '.api_get_course_url()); + exit; +} + +$lrsUrl = $toolLaunch->getLrsUrl() ?: $plugin->get(XApiPlugin::SETTING_LRS_URL); +$lrsAuthUsername = $toolLaunch->getLrsAuthUsername() ?: $plugin->get(XApiPlugin::SETTING_LRS_AUTH_USERNAME); +$lrsAuthPassword = $toolLaunch->getLrsAuthPassword() ?: $plugin->get(XApiPlugin::SETTING_LRS_AUTH_PASSWORD); + +$activityLaunchUrl = $toolLaunch->getLaunchUrl().'?' + .http_build_query( + [ + 'endpoint' => trim($lrsUrl, "/ \t\n\r\0\x0B"), + 'auth' => 'Basic '.base64_encode(trim($lrsAuthUsername).':'.trim($lrsAuthPassword)), + 'actor' => Serializer::createSerializer()->serialize($actor, 'json'), + 'registration' => $attemptId, + 'activity_id' => $toolLaunch->getActivityId(), + ], + null, + '&', + PHP_QUERY_RFC3986 + ); + +header("Location: $activityLaunchUrl"); diff --git a/plugin/xapi/tincan/stats.php b/plugin/xapi/tincan/stats.php new file mode 100644 index 000000000..b019db5d9 --- /dev/null +++ b/plugin/xapi/tincan/stats.php @@ -0,0 +1,163 @@ +find( + ToolLaunch::class, + $request->query->getInt('id') +); + +if (null === $toolLaunch) { + header('Location: '.api_get_course_url()); + exit; +} + +$course = api_get_course_entity(); +$session = api_get_session_entity(); + +$cidReq = api_get_cidreq(); + +$plugin = XApiPlugin::create(); + +$length = 20; +$page = $request->query->getInt('page', 1); +$start = ($page - 1) * $length; +$countStudentList = CourseManager::get_student_list_from_course_code( + $course->getCode(), + (bool) $session, + $session ? $session->getId() : 0, + null, + null, + true, + 0, + true +); + +$statsUrl = api_get_self().'?'.api_get_cidreq().'&id='.$toolLaunch->getId(); + +$paginator = new Paginator(); +$pagination = $paginator->paginate([]); +$pagination->setTotalItemCount($countStudentList); +$pagination->setItemNumberPerPage($length); +$pagination->setCurrentPageNumber($page); +$pagination->renderer = function ($data) use ($statsUrl) { + $render = ''; + if ($data['pageCount'] > 1) { + $render = '
      '; + for ($i = 1; $i <= $data['pageCount']; $i++) { + $pageContent = '
    • '.$i.'
    • '; + if ($data['current'] == $i) { + $pageContent = '
    • '.$i.'
    • '; + } + $render .= $pageContent; + } + $render .= '
    '; + } + + return $render; +}; + +$students = CourseManager::get_student_list_from_course_code( + $course->getCode(), + (bool) $session, + $session ? $session->getId() : 0, + null, + null, + true, + 0, + false, + $start, + $length +); + +$content = ''; +$content .= '
    '; + +$loadingMessage = Display::returnFontAwesomeIcon('spinner', '', true, 'fa-pulse').' '.get_lang('Loading'); + +foreach ($students as $studentInfo) { + $content .= Display::panelCollapse( + api_get_person_name($studentInfo['firstname'], $studentInfo['lastname']), + $loadingMessage, + "pnl-student-{$studentInfo['id']}", + [ + 'class' => 'pnl-student', + 'data-student' => $studentInfo['id'], + 'data-tool' => $toolLaunch->getId(), + ], + "pnl-student-{$studentInfo['id']}-accordion", + "pnl-student-{$studentInfo['id']}-collapse", + false + ); +} + +$content .= '
    '; +$content .= $pagination; + +// View +$interbreadcrumb[] = [ + 'name' => $plugin->get_title(), + 'url' => '../start.php', +]; + +$htmlHeadXtra[] = ""; + +$actions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + "../start.php?$cidReq" +); + +$view = new Template($toolLaunch->getTitle()); +$view->assign( + 'actions', + Display::toolbarAction('xapi_actions', [$actions]) +); +$view->assign('header', $toolLaunch->getTitle()); +$view->assign('content', $content); +$view->display_one_col_template(); diff --git a/plugin/xapi/tincan/stats_attempts.ajax.php b/plugin/xapi/tincan/stats_attempts.ajax.php new file mode 100644 index 000000000..8339518d9 --- /dev/null +++ b/plugin/xapi/tincan/stats_attempts.ajax.php @@ -0,0 +1,131 @@ +isXmlHttpRequest() + || !api_is_allowed_to_edit() + || !$course +) { + echo Display::return_message(get_lang('NotAllowed'), 'error'); + exit; +} + +$plugin = XApiPlugin::create(); +$em = Database::getManager(); + +$toolLaunch = $em->find( + ToolLaunch::class, + $request->request->getInt('tool') +); + +$student = api_get_user_entity($request->request->getInt('student')); + +if (!$toolLaunch || !$student) { + echo Display::return_message(get_lang('NotAllowed'), 'error'); + exit; +} + +$userIsSubscribedToCourse = CourseManager::is_user_subscribed_in_course( + $student->getId(), + $course->getCode(), + (bool) $session, + $session ? $session->getId() : 0 +); + +if (!$userIsSubscribedToCourse) { + echo Display::return_message(get_lang('NotAllowed'), 'error'); + exit; +} + +$cidReq = api_get_cidreq(); + +$xApiStateClient = $plugin->getXApiStateClient( + $toolLaunch->getLrsUrl(), + $toolLaunch->getLrsAuthUsername(), + $toolLaunch->getLrsAuthPassword() +); + +$activity = new Activity( + IRI::fromString($toolLaunch->getActivityId()) +); + +$actor = new Agent( + InverseFunctionalIdentifier::withMbox( + IRI::fromString('mailto:'.$student->getEmail()) + ), + $student->getCompleteName() +); + +try { + $stateDocument = $xApiStateClient->getDocument( + new State( + $activity, + $actor, + $plugin->generateIri('tool-'.$toolLaunch->getId(), 'state')->getValue() + ) + ); +} catch (NotFoundException $notFoundException) { + echo Display::return_message(get_lang('NoResults'), 'warning'); + exit; +} catch (XApiException $exception) { + echo Display::return_message($exception->getMessage(), 'error'); + exit; +} + +$content = ''; + +if ($stateDocument) { + $i = 1; + + foreach ($stateDocument->getData()->getData() as $attemptId => $attempt) { + $firstLaunch = api_convert_and_format_date( + $attempt[XApiPlugin::STATE_FIRST_LAUNCH], + DATE_TIME_FORMAT_LONG + ); + $lastLaunch = api_convert_and_format_date( + $attempt[XApiPlugin::STATE_LAST_LAUNCH], + DATE_TIME_FORMAT_LONG + ); + + $content .= '
    ' + .'
    '.$plugin->get_lang('ActivityFirstLaunch').'
    ' + .'
    '.$firstLaunch.'
    ' + .'
    '.$plugin->get_lang('ActivityLastLaunch').'
    ' + .'
    '.$lastLaunch.'
    ' + .'
    ' + .Display::toolbarButton( + get_lang('ShowAllAttempts'), + '#', + 'th-list', + 'default', + [ + 'class' => 'btn_xapi_attempt_detail', + 'data-attempt' => $attemptId, + 'data-tool' => $toolLaunch->getId(), + 'style' => 'margin-bottom: 20px; margin-left: 180px;', + 'role' => 'button', + ] + ); + + $i++; + } +} + +echo $content; diff --git a/plugin/xapi/tincan/stats_statements.ajax.php b/plugin/xapi/tincan/stats_statements.ajax.php new file mode 100644 index 000000000..6d14bf046 --- /dev/null +++ b/plugin/xapi/tincan/stats_statements.ajax.php @@ -0,0 +1,113 @@ +isXmlHttpRequest() + || !api_is_allowed_to_edit() + || !$course +) { + echo Display::return_message(get_lang('NotAllowed'), 'error'); + exit; +} + +$plugin = XApiPlugin::create(); +$em = Database::getManager(); + +$toolLaunch = $em->find( + ToolLaunch::class, + $request->request->getInt('tool') +); + +$attempt = $request->request->get('attempt'); + +if (!$toolLaunch || !$attempt) { + echo Display::return_message(get_lang('NoResults'), 'error'); + exit; +} + +$cidReq = api_get_cidreq(); + +$xapiStatementClient = $plugin->getXApiStatementClient(); + +$activity = new Activity( + IRI::fromString($toolLaunch->getActivityId()) +); + +$filter = new StatementsFilter(); +$filter + ->byRegistration($attempt); + +try { + $result = $xapiStatementClient->getStatements($filter); +} catch (XApiException $xApiException) { + echo Display::return_message($xApiException->getMessage(), 'error'); + exit; +} catch (Exception $exception) { + echo Display::return_message($exception->getMessage(), 'error'); + exit; +} + +$statements = $result->getStatements(); + +if (count($statements) <= 0) { + echo Display::return_message(get_lang('NoResults'), 'warning'); + exit; +} + +$table = new HTML_Table(['class' => 'table table-condensed table-bordered table-striped table-hover']); +$table->setHeaderContents(0, 0, get_lang('CreatedAt')); +$table->setHeaderContents(0, 1, $plugin->get_lang('Actor')); +$table->setHeaderContents(0, 2, $plugin->get_lang('Verb')); +$table->setHeaderContents(0, 3, $plugin->get_lang('ActivityId')); + +$i = 1; + +$languageIso = api_get_language_isocode(api_get_interface_language()); + +foreach ($statements as $statement) { + $timestampStored = $statement->getCreated() ? api_convert_and_format_date($statement->getCreated()) : '-'; + $actor = $statement->getActor()->getName(); + $verb = XApiPlugin::extractVerbInLanguage($statement->getVerb()->getDisplay(), $languageIso); + $activity = ''; + + $statementObject = $statement->getObject(); + + if ($statementObject instanceof Activity) { + if (null !== $statementObject->getDefinition()) { + $definition = $statementObject->getDefinition(); + + if (null !== $definition->getName()) { + $activity = XApiPlugin::extractVerbInLanguage($definition->getName(), $languageIso).'
    '; + } + } + + $activity .= Display::tag( + 'small', + $statementObject->getId()->getValue(), + ['class' => 'text-muted'] + ); + } + + $table->setCellContents($i, 0, $timestampStored); + $table->setCellContents($i, 1, $actor); + $table->setCellContents($i, 2, $verb); + $table->setCellContents($i, 3, $activity); + + $i++; +} + +$table->display(); diff --git a/plugin/xapi/tincan/view.php b/plugin/xapi/tincan/view.php new file mode 100644 index 000000000..9c259d7f3 --- /dev/null +++ b/plugin/xapi/tincan/view.php @@ -0,0 +1,192 @@ +find( + ToolLaunch::class, + $request->query->getInt('id') +); + +if (null === $toolLaunch + || $toolLaunch->getCourse()->getId() !== api_get_course_entity()->getId() +) { + api_not_allowed(true); +} + +$plugin = XApiPlugin::create(); + +$activity = new Activity( + IRI::fromString($toolLaunch->getActivityId()) +); +$actor = new Agent( + InverseFunctionalIdentifier::withMbox( + IRI::fromString('mailto:'.$user->getEmail()) + ), + $user->getCompleteName() +); +$state = new State( + $activity, + $actor, + $plugin->generateIri('tool-'.$toolLaunch->getId(), 'state')->getValue() +); + +$cidReq = api_get_cidreq(); + +try { + $stateDocument = $plugin + ->getXApiStateClient( + $toolLaunch->getLrsUrl(), + $toolLaunch->getLrsAuthUsername(), + $toolLaunch->getLrsAuthPassword() + ) + ->getDocument($state); +} catch (NotFoundException $notFoundException) { + $stateDocument = null; +} catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + + header('Location: '.api_get_course_url()); + exit; +} + +$formTarget = $originIsLearnpath ? '_self' : '_blank'; + +$frmNewRegistration = new FormValidator( + 'launch_new', + 'post', + "launch.php?$cidReq", + '', + ['target' => $formTarget], + FormValidator::LAYOUT_INLINE +); +$frmNewRegistration->addHidden('attempt_id', Uuid::uuid4()); +$frmNewRegistration->addHidden('id', $toolLaunch->getId()); +$frmNewRegistration->addButton( + 'submit', + $plugin->get_lang('LaunchNewAttempt'), + 'external-link fa-fw', + 'success' +); + +if ($stateDocument) { + $row = 0; + + $table = new HTML_Table(['class' => 'table table-hover table-striped']); + $table->setHeaderContents($row, 0, $plugin->get_lang('ActivityFirstLaunch')); + $table->setHeaderContents($row, 1, $plugin->get_lang('ActivityLastLaunch')); + $table->setHeaderContents($row, 2, get_lang('Actions')); + + $row++; + + $langActivityLaunch = $plugin->get_lang('ActivityLaunch'); + + foreach ($stateDocument->getData()->getData() as $attemptId => $attempt) { + $firstLaunch = api_convert_and_format_date( + $attempt[XApiPlugin::STATE_FIRST_LAUNCH], + DATE_TIME_FORMAT_LONG + ); + $lastLaunch = api_convert_and_format_date( + $attempt[XApiPlugin::STATE_LAST_LAUNCH], + DATE_TIME_FORMAT_LONG + ); + + $frmLaunch = new FormValidator( + "launch_$row", + 'post', + "launch.php?$cidReq", + '', + ['target' => $formTarget], + FormValidator::LAYOUT_INLINE + ); + $frmLaunch->addHidden('attempt_id', $attemptId); + $frmLaunch->addHidden('id', $toolLaunch->getId()); + $frmLaunch->addButton( + 'submit', + $langActivityLaunch, + 'external-link fa-fw', + 'default' + ); + + $table->setCellContents($row, 0, $firstLaunch); + $table->setCellContents($row, 1, $lastLaunch); + $table->setCellContents($row, 2, $frmLaunch->returnForm()); + + $row++; + } + + $table->setColAttributes(0, ['class' => 'text-center']); + $table->setColAttributes(1, ['class' => 'text-center']); + $table->setColAttributes(2, ['class' => 'text-center']); +} + +$interbreadcrumb[] = ['url' => '../start.php', 'name' => $plugin->get_lang('ToolTinCan')]; + +$pageTitle = $toolLaunch->getTitle(); +$pageContent = ''; + +if ($toolLaunch->getDescription()) { + $pageContent .= PHP_EOL; + $pageContent .= "

    {$toolLaunch->getDescription()}

    "; +} + +if ($toolLaunch->isAllowMultipleAttempts() + || empty($stateDocument) +) { + $pageContent .= Display::div( + $frmNewRegistration->returnForm(), + ['class' => 'exercise_overview_options'] + ); +} + +if ($stateDocument) { + $pageContent .= $table->toHtml(); +} + +$actions = ''; + +if (!$originIsLearnpath) { + $actions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + '../start.php?'.api_get_cidreq() + ); +} + +$view = new Template($pageTitle); +$view->assign('header', $pageTitle); + +if ($actions) { + $view->assign( + 'actions', + Display::toolbarAction( + 'xapi_actions', + [$actions] + ) + ); +} +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/tool_delete.php b/plugin/xapi/tool_delete.php new file mode 100644 index 000000000..910e4a765 --- /dev/null +++ b/plugin/xapi/tool_delete.php @@ -0,0 +1,39 @@ +find( + ToolLaunch::class, + $request->query->getInt('delete') +); + +if (null === $toolLaunch + || $toolLaunch->getCourse()->getId() !== api_get_course_entity()->getId() +) { + api_not_allowed(true); +} + +$plugin = XApiPlugin::create(); + +$em = Database::getManager(); +$em->remove($toolLaunch); +$em->flush(); + +Display::addFlash( + Display::return_message($plugin->get_lang('ActivityDeleted'), 'success') +); + +header('Location: '.api_get_course_url()); +exit; diff --git a/plugin/xapi/tool_edit.php b/plugin/xapi/tool_edit.php new file mode 100644 index 000000000..fec53f7b3 --- /dev/null +++ b/plugin/xapi/tool_edit.php @@ -0,0 +1,145 @@ +find( + ToolLaunch::class, + $request->query->getInt('edit') +); + +if (null === $toolLaunch) { + header('Location: '.api_get_course_url()); + exit; +} + +$course = api_get_course_entity(); +$session = api_get_session_entity(); + +$cidReq = api_get_cidreq(); + +$plugin = XApiPlugin::create(); + +$toolIsCmi5 = 'cmi5' === $toolLaunch->getActivityType(); +$toolIsTinCan = !$toolIsCmi5; + +$langEditActivity = $plugin->get_lang('EditActivity'); + +$frmActivity = new FormValidator('frm_activity', 'post', api_get_self()."?$cidReq&edit={$toolLaunch->getId()}"); +$frmActivity->addText('title', get_lang('Title')); +$frmActivity->addTextarea('description', get_lang('Description')); + +if ($toolIsTinCan) { + $frmActivity->addButtonAdvancedSettings('advanced_params'); + $frmActivity->addHtml(''); +} + +$frmActivity->addButtonAdvancedSettings('lrs_params', $plugin->get_lang('LrsConfiguration')); +$frmActivity->addHtml(''); +$frmActivity->addButtonUpdate(get_lang('Update')); +$frmActivity->applyFilter('title', 'trim'); +$frmActivity->applyFilter('description', 'trim'); +$frmActivity->applyFilter('lrs_url', 'trim'); +$frmActivity->applyFilter('lrs_auth', 'trim'); + +if ($frmActivity->validate()) { + $values = $frmActivity->exportValues(); + + $toolLaunch + ->setTitle($values['title']) + ->setDescription(empty($values['description']) ? null : $values['description']); + + if ($toolIsTinCan && isset($values['allow_multiple_attempts'])) { + $toolLaunch->setAllowMultipleAttempts(true); + } + + if (!empty($values['lrs_url']) + && !empty($values['lrs_auth_username']) + && !empty($values['lrs_auth_password']) + ) { + $toolLaunch + ->setLrsUrl($values['lrs_url']) + ->setLrsAuthUsername($values['lrs_auth_username']) + ->setLrsAuthPassword($values['lrs_auth_password']); + } + + $em->persist($toolLaunch); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('ActivityUpdated'), 'success') + ); + + header('Location: '.api_get_course_url()); + exit; +} + +$frmActivity->setDefaults( + [ + 'title' => $toolLaunch->getTitle(), + 'description' => $toolLaunch->getDescription(), + 'allow_multiple_attempts' => $toolLaunch->isAllowMultipleAttempts(), + 'lrs_url' => $toolLaunch->getLrsUrl(), + 'lrs_auth_username' => $toolLaunch->getLrsAuthUsername(), + 'lrs_auth_password' => $toolLaunch->getLrsAuthPassword(), + ] +); + +$actions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + 'start.php?'.api_get_cidreq() +); + +$pageContent = $frmActivity->returnForm(); + +$interbreadcrumb[] = ['url' => 'start.php', 'name' => $plugin->get_lang('ToolTinCan')]; + +$view = new Template($langEditActivity); +$view->assign('header', $langEditActivity); +$view->assign( + 'actions', + Display::toolbarAction( + 'xapi_actions', + [$actions] + ) +); +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/tool_import.php b/plugin/xapi/tool_import.php new file mode 100644 index 000000000..7c9fedc59 --- /dev/null +++ b/plugin/xapi/tool_import.php @@ -0,0 +1,177 @@ +query->has('lp_id')) { + $lp = new learnpath('', $httpRequest->query->getInt('lp_id'), $userId); + + if (!empty($lp->lp_id)) { + $pluginIndex = api_get_path(WEB_CODE_PATH)."lp/lp_controller.php?$cidReq&" + .http_build_query(['action' => 'add_item', 'type' => 'step', 'lp' => $lp->lp_id, 'lp_build_selected' => 8]); + } +} + +$langAddActivity = $plugin->get_lang('AddActivity'); + +$formAction = api_get_self()."?$cidReq&".($lp ? http_build_query(['lp_id' => $lp->lp_id]) : ''); + +$frmActivity = new FormValidator('frm_activity', 'post', $formAction); +$frmActivity->addFile('file', $plugin->get_lang('XApiPackage')); +$frmActivity->addButtonAdvancedSettings('advanced_params'); +$frmActivity->addHtml(''); +$frmActivity->addButtonAdvancedSettings('lrs_params', $plugin->get_lang('LrsConfiguration')); +$frmActivity->addHtml(''); +$frmActivity->addButtonImport(get_lang('Import')); +$frmActivity->addRule('file', get_lang('ThisFileIsRequired'), 'required'); +$frmActivity->addRule( + 'file', + $plugin->get_lang('OnlyZipOrXmlAllowed'), + 'filetype', + ['zip', 'xml'] +); +$frmActivity->applyFilter('title', 'trim'); +$frmActivity->applyFilter('description', 'trim'); +$frmActivity->applyFilter('lrs_url', 'trim'); +$frmActivity->applyFilter('lrs_auth', 'trim'); + +if ($frmActivity->validate()) { + $values = $frmActivity->exportValues(); + $zipFileInfo = $_FILES['file']; + + try { + $importer = PackageImporter::create($zipFileInfo, $course); + $packageFile = $importer->import(); + + $parser = PackageParser::create( + $importer->getPackageType(), + $packageFile, + $course, + $session + ); + $toolLaunch = $parser->parse(); + } catch (Exception $e) { + Display::addFlash( + Display::return_message($e->getMessage(), 'error') + ); + + header("Location: $pluginIndex"); + exit; + } + + if ('tincan' === $importer->getPackageType() && isset($values['allow_multiple_attempts'])) { + $toolLaunch->setAllowMultipleAttempts(true); + } + + if (!empty($values['title'])) { + $toolLaunch->setTitle($values['title']); + } + + if (!empty($values['description'])) { + $toolLaunch->setDescription($values['description']); + } + + if (!empty($values['lrs_url']) + && !empty($values['lrs_auth_username']) + && !empty($values['lrs_auth_password']) + ) { + $toolLaunch + ->setLrsUrl($values['lrs_url']) + ->setLrsAuthUsername($values['lrs_auth_username']) + ->setLrsAuthPassword($values['lrs_auth_password']); + } + + $em = Database::getManager(); + $em->persist($toolLaunch); + $em->flush(); + + Display::addFlash( + Display::return_message($plugin->get_lang('ActivityImported'), 'success') + ); + + header("Location: $pluginIndex"); + exit; +} + +$frmActivity->setDefaults(['allow_multiple_attempts' => true]); + +$actions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $pluginIndex +); + +$pageContent = $frmActivity->returnForm(); + +if ($lp) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?action=list&'.api_get_cidreq(), + 'name' => get_lang('LearningPaths'), + ]; + $interbreadcrumb[] = [ + 'url' => $pluginIndex, + 'name' => $lp->getNameNoTags(), + ]; +} else { + $interbreadcrumb[] = ['url' => $pluginIndex, 'name' => $plugin->get_lang('ToolTinCan')]; +} + +$view = new Template($langAddActivity); +$view->assign('header', $langAddActivity); +$view->assign( + 'actions', + Display::toolbarAction( + 'xapi_actions', + [$actions] + ) +); +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/uninstall.php b/plugin/xapi/uninstall.php new file mode 100644 index 000000000..3bcf57247 --- /dev/null +++ b/plugin/xapi/uninstall.php @@ -0,0 +1,5 @@ +uninstall(); diff --git a/plugin/xapi/views/cmi5_launch.twig b/plugin/xapi/views/cmi5_launch.twig new file mode 100644 index 000000000..8aa5f8359 --- /dev/null +++ b/plugin/xapi/views/cmi5_launch.twig @@ -0,0 +1,15 @@ +
    +
    +
    +

    {{ tool.title }}

    +

    {{ tool.description }}

    + +
    + + {{ toc_html }} +
    +
    + +
    +
    +
    diff --git a/plugin/zoom/Entity/Meeting.php b/plugin/zoom/Entity/Meeting.php new file mode 100644 index 000000000..f2995f385 --- /dev/null +++ b/plugin/zoom/Entity/Meeting.php @@ -0,0 +1,706 @@ +registrants = new ArrayCollection(); + $this->recordings = new ArrayCollection(); + $this->activities = new ArrayCollection(); + $this->signAttendance = false; + $this->sysAnnouncement = null; + } + + /** + * @return string + */ + public function __toString() + { + return sprintf('Meeting %d', $this->id); + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return int + */ + public function getMeetingId() + { + return $this->meetingId; + } + + /** + * @param int $meetingId + * + * @return Meeting + */ + public function setMeetingId($meetingId) + { + $this->meetingId = $meetingId; + + return $this; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @return Course + */ + public function getCourse() + { + return $this->course; + } + + /** + * @return Session + */ + public function getSession() + { + return $this->session; + } + + /** + * @return Registrant[]|ArrayCollection + */ + public function getRegistrants() + { + return $this->registrants->filter(function (Registrant $registrant) { + return !$registrant instanceof Presenter; + }); + } + + /** + * @return ArrayCollection + */ + public function getPresenters(): ArrayCollection + { + return $this->registrants->filter(function (Registrant $registrant) { + return $registrant instanceof Presenter; + }); + } + + public function hasUserAsPresenter(User $user): bool + { + $presenters = $this->getPresenters(); + + $criteria = Criteria::create(); + $criteria->where( + Criteria::expr()->eq('user', $user) + ); + + return $presenters->matching($criteria)->count() > 0; + } + + /** + * @return Recording[]|ArrayCollection + */ + public function getRecordings() + { + return $this->recordings; + } + + /** + * @return MeetingActivity[]|ArrayCollection + */ + public function getActivities() + { + return $this->activities; + } + + public function addActivity(MeetingActivity $activity) + { + $activity->setMeeting($this); + $this->activities[] = $activity; + } + + /** + * @param MeetingActivity[]|ArrayCollection $activities + * + * @return Meeting + */ + public function setActivities($activities) + { + $this->activities = $activities; + + return $this; + } + + /** + * @ORM\PostLoad + * + * @throws Exception + */ + public function postLoad() + { + if (null !== $this->meetingListItemJson) { + $this->meetingListItem = MeetingListItem::fromJson($this->meetingListItemJson); + } + if (null !== $this->meetingInfoGetJson) { + $this->meetingInfoGet = MeetingInfoGet::fromJson($this->meetingInfoGetJson); + } + $this->initializeDisplayableProperties(); + } + + /** + * @ORM\PostUpdate + * + * @throws Exception + */ + public function postUpdate() + { + $this->initializeDisplayableProperties(); + } + + /** + * @ORM\PreFlush + */ + public function preFlush() + { + if (null !== $this->meetingListItem) { + $this->meetingListItemJson = json_encode($this->meetingListItem); + } + if (null !== $this->meetingInfoGet) { + $this->meetingInfoGetJson = json_encode($this->meetingInfoGet); + } + } + + /** + * @return MeetingListItem + */ + public function getMeetingListItem() + { + return $this->meetingListItem; + } + + /** + * @return MeetingInfoGet + */ + public function getMeetingInfoGet() + { + return $this->meetingInfoGet; + } + + /** + * @param User $user + * + * @return $this + */ + public function setUser($user) + { + $this->user = $user; + + return $this; + } + + /** + * @param Course $course + * + * @return $this + */ + public function setCourse($course) + { + $this->course = $course; + + return $this; + } + + /** + * @param Session $session + * + * @return $this + */ + public function setSession($session) + { + $this->session = $session; + + return $this; + } + + /** + * @return CGroupInfo + */ + public function getGroup() + { + return $this->group; + } + + /** + * @param CGroupInfo $group + * + * @return Meeting + */ + public function setGroup($group) + { + $this->group = $group; + + return $this; + } + + /** + * @param MeetingListItem $meetingListItem + * + * @throws Exception + * + * @return Meeting + */ + public function setMeetingListItem($meetingListItem) + { + if (null === $this->meetingId) { + $this->meetingId = $meetingListItem->id; + } elseif ($this->meetingId != $meetingListItem->id) { + throw new Exception('the Meeting identifier differs from the MeetingListItem identifier'); + } + $this->meetingListItem = $meetingListItem; + + return $this; + } + + /** + * @param MeetingInfoGet|BaseMeetingTrait $meetingInfoGet + * + * @throws Exception + * + * @return Meeting + */ + public function setMeetingInfoGet($meetingInfoGet) + { + if (null === $this->meetingId) { + $this->meetingId = $meetingInfoGet->id; + } elseif ($this->meetingId != $meetingInfoGet->id) { + throw new Exception('the Meeting identifier differs from the MeetingInfoGet identifier'); + } + $this->meetingInfoGet = $meetingInfoGet; + $this->initializeDisplayableProperties(); + + return $this; + } + + /** + * @return bool + */ + public function isCourseMeeting() + { + return null !== $this->course; + } + + /** + * @return bool + */ + public function isCourseGroupMeeting() + { + return null !== $this->course && null !== $this->group; + } + + /** + * @return bool + */ + public function isUserMeeting() + { + return null !== $this->user && null === $this->course; + } + + /** + * @return bool + */ + public function isGlobalMeeting() + { + return null === $this->user && null === $this->course; + } + + public function setStatus($status) + { + $this->meetingInfoGet->status = $status; + } + + /** + * Builds the list of users that can register into this meeting. + * Zoom requires an email address, therefore users without an email address are excluded from the list. + * + * @return User[] the list of users + */ + public function getRegistrableUsers() + { + $users = []; + if (!$this->isCourseMeeting()) { + $criteria = ['active' => true]; + $users = Database::getManager()->getRepository('ChamiloUserBundle:User')->findBy($criteria); + } elseif (null === $this->session) { + if (null !== $this->course) { + /** @var CourseRelUser $courseRelUser */ + foreach ($this->course->getUsers() as $courseRelUser) { + $users[] = $courseRelUser->getUser(); + } + } + } else { + if (null !== $this->course) { + $subscriptions = $this->session->getUserCourseSubscriptionsByStatus($this->course, Session::STUDENT); + if ($subscriptions) { + /** @var SessionRelCourseRelUser $sessionCourseUser */ + foreach ($subscriptions as $sessionCourseUser) { + $users[] = $sessionCourseUser->getUser(); + } + } + } + } + + $activeUsersWithEmail = []; + foreach ($users as $user) { + if ($user->isActive() && !empty($user->getEmail())) { + $activeUsersWithEmail[] = $user; + } + } + + return $activeUsersWithEmail; + } + + /** + * @return bool + */ + public function requiresDateAndDuration() + { + return MeetingInfoGet::TYPE_SCHEDULED === $this->meetingInfoGet->type + || MeetingInfoGet::TYPE_RECURRING_WITH_FIXED_TIME === $this->meetingInfoGet->type; + } + + public function requiresRegistration(): bool + { + return true; //MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE === $this->meetingInfoGet->settings->approval_type; + /*return + MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED != $this->meetingInfoGet->settings->approval_type;*/ + } + + /** + * @return bool + */ + public function hasCloudAutoRecordingEnabled() + { + return \ZoomPlugin::RECORDING_TYPE_NONE !== $this->meetingInfoGet->settings->auto_recording; + } + + public function getRegistrantByUser(User $user): ?Registrant + { + $criteria = Criteria::create() + ->where( + Criteria::expr()->eq('user', $user) + ) + ; + + return $this->registrants->matching($criteria)->first() ?: null; + } + + /** + * Generates a short presentation of the meeting for the future participant. + * To be displayed above the "Enter meeting" link. + * + * @return string + */ + public function getIntroduction() + { + $introduction = sprintf('

    %s

    ', $this->getTopic()).PHP_EOL; + if (!$this->isGlobalMeeting()) { + if (!empty($this->formattedStartTime)) { + $introduction .= $this->formattedStartTime; + if (!empty($this->formattedDuration)) { + $introduction .= ' ('.$this->formattedDuration.')'; + } + $introduction .= PHP_EOL; + } + } + if ($this->user) { + $introduction .= sprintf('

    %s

    ', $this->user->getFullname()).PHP_EOL; + } elseif ($this->isCourseMeeting()) { + if (null === $this->session) { + $introduction .= sprintf('

    %s

    ', $this->course).PHP_EOL; + } else { + $introduction .= sprintf('

    %s (%s)

    ', $this->course, $this->session).PHP_EOL; + } + } + + if (!empty($this->getAgenda())) { + $introduction .= sprintf('

    %s

    ', $this->getAgenda()).PHP_EOL; + } + + return $introduction; + } + + public function isSignAttendance(): bool + { + return $this->signAttendance; + } + + public function setSignAttendance(bool $signAttendance): Meeting + { + $this->signAttendance = $signAttendance; + + return $this; + } + + public function getAccountEmail(): ?string + { + return $this->accountEmail; + } + + public function setAccountEmail(?string $accountEmail): self + { + $this->accountEmail = $accountEmail; + + return $this; + } + + public function getReasonToSignAttendance(): ?string + { + return $this->reasonToSignAttendance; + } + + public function setReasonToSignAttendance(string $reasonToSignAttendance): Meeting + { + $this->reasonToSignAttendance = $reasonToSignAttendance; + + return $this; + } + + public function getTopic(): string + { + return $this->meetingInfoGet->topic; + } + + public function getAgenda(): ?string + { + return $this->meetingInfoGet->agenda; + } + + public function getSysAnnouncement(): ?SysAnnouncement + { + return $this->sysAnnouncement; + } + + public function setSysAnnouncement(?SysAnnouncement $sysAnnouncement): Meeting + { + $this->sysAnnouncement = $sysAnnouncement; + + return $this; + } + + /** + * @throws Exception on unexpected start_time or duration + */ + protected function initializeDisplayableProperties() + { + $zoomPlugin = new \ZoomPlugin(); + + $typeList = [ + API\Meeting::TYPE_INSTANT => $zoomPlugin->get_lang('InstantMeeting'), + API\Meeting::TYPE_SCHEDULED => $zoomPlugin->get_lang('ScheduledMeeting'), + API\Meeting::TYPE_RECURRING_WITH_NO_FIXED_TIME => $zoomPlugin->get_lang('RecurringWithNoFixedTime'), + API\Meeting::TYPE_RECURRING_WITH_FIXED_TIME => $zoomPlugin->get_lang('RecurringWithFixedTime'), + ]; + $this->typeName = $typeList[$this->meetingInfoGet->type]; + + if (property_exists($this, 'status')) { + $statusList = [ + 'waiting' => $zoomPlugin->get_lang('Waiting'), + 'started' => $zoomPlugin->get_lang('Started'), + 'finished' => $zoomPlugin->get_lang('Finished'), + ]; + $this->statusName = $statusList[$this->meetingInfoGet->status]; + } + $this->startDateTime = null; + $this->formattedStartTime = ''; + $this->durationInterval = null; + $this->formattedDuration = ''; + if (!empty($this->meetingInfoGet->start_time)) { + $this->startDateTime = new DateTime($this->meetingInfoGet->start_time); + $this->startDateTime->setTimezone(new DateTimeZone(api_get_timezone())); + $this->formattedStartTime = $this->startDateTime->format('Y-m-d H:i'); + } + + if (!empty($this->meetingInfoGet->duration)) { + $now = new DateTime(); + $later = new DateTime(); + $later->add(new DateInterval('PT'.$this->meetingInfoGet->duration.'M')); + $this->durationInterval = $now->diff($later); + $this->formattedDuration = $this->durationInterval->format($zoomPlugin->get_lang('DurationFormat')); + } + } +} diff --git a/plugin/zoom/Entity/MeetingActivity.php b/plugin/zoom/Entity/MeetingActivity.php new file mode 100644 index 000000000..fdeaf84c5 --- /dev/null +++ b/plugin/zoom/Entity/MeetingActivity.php @@ -0,0 +1,208 @@ +createdAt = new \DateTime(); + } + + /** + * @return string + */ + public function __toString() + { + return sprintf('Activity %d', $this->id); + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return Meeting + */ + public function getMeeting() + { + return $this->meeting; + } + + /** + * @param Meeting $meeting + * + * @return MeetingActivity + */ + public function setMeeting($meeting) + { + $this->meeting = $meeting; + + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + * + * @return MeetingActivity + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $type + * + * @return MeetingActivity + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @return DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * @return string + */ + public function getEvent() + { + return $this->event; + } + + /** + * @return Meeting + */ + public function getUser() + { + return $this->user; + } + + /** + * @param Meeting $user + * + * @return MeetingActivity + */ + public function setUser($user) + { + $this->user = $user; + + return $this; + } + + public function getEventDecoded() + { + if (!empty($this->event)) { + return json_decode($this->event); + } + + return ''; + } + + /** + * @param string $event + * + * @return MeetingActivity + */ + public function setEvent($event) + { + $this->event = $event; + + return $this; + } +} diff --git a/plugin/zoom/Entity/Presenter.php b/plugin/zoom/Entity/Presenter.php new file mode 100644 index 000000000..21b417fae --- /dev/null +++ b/plugin/zoom/Entity/Presenter.php @@ -0,0 +1,38 @@ +id); + } + + /** + * @ORM\PostLoad() + * + * @throws Exception + */ + public function postLoad() + { + parent::postLoad(); + } + + /** + * @ORM\PreFlush() + */ + public function preFlush() + { + parent::preFlush(); + } +} diff --git a/plugin/zoom/Entity/Recording.php b/plugin/zoom/Entity/Recording.php new file mode 100644 index 000000000..ac6f1f551 --- /dev/null +++ b/plugin/zoom/Entity/Recording.php @@ -0,0 +1,193 @@ +getRecordingMeeting(); + if (property_exists($object, $name)) { + return $object->$name; + } + throw new Exception(sprintf('%s does not know property %s', $this, $name)); + } + + /** + * @return string + */ + public function __toString() + { + return sprintf('Recording %d', $this->uuid); + } + + /** + * @return Meeting + */ + public function getMeeting() + { + return $this->meeting; + } + + /** + * @throws Exception + * + * @return RecordingMeeting + */ + public function getRecordingMeeting() + { + return $this->recordingMeeting; + } + + /** + * @param Meeting $meeting + * + * @return $this + */ + public function setMeeting($meeting) + { + $this->meeting = $meeting; + $this->meeting->getRecordings()->add($this); + + return $this; + } + + /** + * @param RecordingMeeting $recordingMeeting + * + * @throws Exception + * + * @return Recording + */ + public function setRecordingMeeting($recordingMeeting) + { + if (null === $this->uuid) { + $this->uuid = $recordingMeeting->uuid; + } elseif ($this->uuid !== $recordingMeeting->uuid) { + throw new Exception('the RecordingEntity identifier differs from the RecordingMeeting identifier'); + } + if (null === $this->meeting) { + $this->meeting = Database::getManager()->getRepository(Meeting::class)->find($recordingMeeting->id); + } elseif ($this->meeting->getMeetingId() != $recordingMeeting->id) { + // $this->meeting remains null when the remote RecordingMeeting refers to a deleted meeting. + throw new Exception('The RecordingEntity meeting id differs from the RecordingMeeting meeting id'); + } + $this->recordingMeeting = $recordingMeeting; + + return $this; + } + + /** + * @ORM\PostLoad + * + * @throws Exception + */ + public function postLoad() + { + if (null !== $this->recordingMeetingJson) { + $this->recordingMeeting = RecordingMeeting::fromJson($this->recordingMeetingJson); + } + $this->initializeExtraProperties(); + } + + /** + * @ORM\PreFlush + */ + public function preFlush() + { + if (null !== $this->recordingMeeting) { + $this->recordingMeetingJson = json_encode($this->recordingMeeting); + } + } + + /** + * @throws Exception + */ + public function initializeExtraProperties() + { + $this->startDateTime = new DateTime($this->recordingMeeting->start_time); + $this->startDateTime->setTimezone(new DateTimeZone(api_get_timezone())); + $this->formattedStartTime = $this->startDateTime->format('Y-m-d H:i'); + + $now = new DateTime(); + $later = new DateTime(); + $later->add(new DateInterval('PT'.$this->recordingMeeting->duration.'M')); + $this->durationInterval = $later->diff($now); + $this->formattedDuration = $this->durationInterval->format(get_lang('DurationFormat')); + } +} diff --git a/plugin/zoom/Entity/Registrant.php b/plugin/zoom/Entity/Registrant.php new file mode 100644 index 000000000..f65bdc787 --- /dev/null +++ b/plugin/zoom/Entity/Registrant.php @@ -0,0 +1,283 @@ +id); + } + + /** + * @return Meeting + */ + public function getMeeting() + { + return $this->meeting; + } + + /** + * @param Meeting $meeting + * + * @return $this + */ + public function setMeeting($meeting) + { + $this->meeting = $meeting; + $this->meeting->getRegistrants()->add($this); + + return $this; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @param User $user + * + * @return $this + */ + public function setUser($user) + { + $this->user = $user; + + return $this; + } + + /** + * @throws Exception + * + * @return MeetingRegistrantListItem + */ + public function getMeetingRegistrantListItem() + { + return $this->meetingRegistrantListItem; + } + + /** + * @param MeetingRegistrantListItem $meetingRegistrantListItem + * + * @throws Exception + * + * @return $this + */ + public function setMeetingRegistrantListItem($meetingRegistrantListItem) + { + if (!is_null($this->meeting) && $this->meeting->getId() != $meetingRegistrantListItem->id) { + throw new Exception('RegistrantEntity meeting id differs from MeetingRegistrantListItem id'); + } + $this->meetingRegistrantListItem = $meetingRegistrantListItem; + $this->computeFullName(); + + return $this; + } + + public function computeFullName() + { + $this->fullName = api_get_person_name( + $this->meetingRegistrant->first_name, + $this->meetingRegistrant->last_name + ); + } + + public function getJoinUrl() + { + if (!$this->createdRegistration) { + return ''; + } + + return $this->createdRegistration->join_url; + } + + /** + * @throws Exception + * + * @return CreatedRegistration + */ + public function getCreatedRegistration() + { + return $this->createdRegistration; + } + + /** + * @param CreatedRegistration $createdRegistration + * + * @throws Exception + * + * @return $this + */ + public function setCreatedRegistration($createdRegistration) + { + if (null === $this->id) { + $this->id = $createdRegistration->registrant_id; + } elseif ($this->id != $createdRegistration->registrant_id) { + throw new Exception('RegistrantEntity id differs from CreatedRegistration identifier'); + } + $this->createdRegistration = $createdRegistration; + + return $this; + } + + /** + * @throws Exception + * + * @return MeetingRegistrant + */ + public function getMeetingRegistrant() + { + return $this->meetingRegistrant; + } + + /** + * @throws Exception + */ + public function setMeetingRegistrant(API\RegistrantSchema $meetingRegistrant): Registrant + { + $this->meetingRegistrant = $meetingRegistrant; + $this->computeFullName(); + + return $this; + } + + /** + * @ORM\PostLoad + * + * @throws Exception + */ + public function postLoad() + { + if (null !== $this->meetingRegistrantJson) { + $this->meetingRegistrant = MeetingRegistrant::fromJson($this->meetingRegistrantJson); + } + if (null !== $this->createdRegistrationJson) { + $this->createdRegistration = CreatedRegistration::fromJson($this->createdRegistrationJson); + } + if (null !== $this->meetingRegistrantListItemJson) { + $this->meetingRegistrantListItem = MeetingRegistrantListItem::fromJson( + $this->meetingRegistrantListItemJson + ); + } + $this->computeFullName(); + } + + /** + * @ORM\PreFlush + */ + public function preFlush() + { + if (null !== $this->meetingRegistrant) { + $this->meetingRegistrantJson = json_encode($this->meetingRegistrant); + } + if (null !== $this->createdRegistration) { + $this->createdRegistrationJson = json_encode($this->createdRegistration); + } + if (null !== $this->meetingRegistrantListItem) { + $this->meetingRegistrantListItemJson = json_encode($this->meetingRegistrantListItem); + } + } + + public function setSignature(Signature $signature): void + { + $this->signature = $signature; + + $signature->setRegistrant($this); + } + + public function getSignature(): ?Signature + { + return $this->signature; + } +} diff --git a/plugin/zoom/Entity/Signature.php b/plugin/zoom/Entity/Signature.php new file mode 100644 index 000000000..1413e4f8d --- /dev/null +++ b/plugin/zoom/Entity/Signature.php @@ -0,0 +1,84 @@ +id; + } + + public function getRegistrant(): Registrant + { + return $this->registrant; + } + + public function setRegistrant(Registrant $registrant): Signature + { + $this->registrant = $registrant; + + return $this; + } + + public function getFile(): string + { + return $this->file; + } + + public function setFile(string $file): Signature + { + $this->file = $file; + + return $this; + } + + public function getRegisteredAt(): DateTime + { + return $this->registeredAt; + } + + public function setRegisteredAt(DateTime $registeredAt): Signature + { + $this->registeredAt = $registeredAt; + + return $this; + } +} diff --git a/plugin/zoom/Entity/Webinar.php b/plugin/zoom/Entity/Webinar.php new file mode 100644 index 000000000..24f29c06f --- /dev/null +++ b/plugin/zoom/Entity/Webinar.php @@ -0,0 +1,138 @@ +webinarSchema) { + $this->webinarSchemaJson = json_encode($this->webinarSchema); + } + } + + public function postLoad() + { + if (null !== $this->webinarSchemaJson) { + $this->webinarSchema = WebinarSchema::fromJson($this->webinarSchemaJson); + } + + $this->initializeDisplayableProperties(); + } + + /** + * @param WebinarSchema|BaseMeetingTrait $webinarSchema + * + * @throws Exception + */ + public function setWebinarSchema(WebinarSchema $webinarSchema): Webinar + { + if (null === $this->meetingId) { + $this->meetingId = $webinarSchema->id; + } elseif ($this->meetingId != $webinarSchema->id) { + throw new Exception('the Meeting identifier differs from the MeetingInfoGet identifier'); + } + + $this->webinarSchema = $webinarSchema; + + $this->initializeDisplayableProperties(); + + return $this; + } + + public function getWebinarSchema(): WebinarSchema + { + return $this->webinarSchema; + } + + public function hasCloudAutoRecordingEnabled(): bool + { + return $this->webinarSchema->settings->auto_recording !== ZoomPlugin::RECORDING_TYPE_NONE; + } + + public function requiresDateAndDuration(): bool + { + return WebinarSchema::TYPE_WEBINAR == $this->webinarSchema->type; + } + + public function requiresRegistration(): bool + { + return in_array( + $this->webinarSchema->settings->approval_type, + [ + WebinarSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE, + WebinarSettings::APPROVAL_TYPE_MANUALLY_APPROVE, + ] + ); + } + + public function getTopic(): string + { + return $this->webinarSchema->topic; + } + + public function getAgenda(): ?string + { + return $this->webinarSchema->agenda; + } + + protected function initializeDisplayableProperties() + { + $zoomPlugin = ZoomPlugin::create(); + + $namedTypes = [ + WebinarSchema::TYPE_WEBINAR => $zoomPlugin->get_lang('Webinar'), + WebinarSchema::TYPE_RECURRING_NO_FIXED_TIME => $zoomPlugin->get_lang('RecurringWithNoFixedTime'), + WebinarSchema::TYPE_RECURRING_FIXED_TIME => $zoomPlugin->get_lang('RecurringWithFixedTime'), + ]; + + $this->typeName = $namedTypes[$this->webinarSchema->type]; + + if ($this->webinarSchema->start_time) { + $this->startDateTime = new DateTime( + $this->webinarSchema->start_time, + new \DateTimeZone(api_get_timezone()) + ); + $this->formattedStartTime = $this->startDateTime->format('Y-m-d H:i'); + } + + if ($this->webinarSchema->duration) { + $now = new DateTime(); + $later = new DateTime(); + $later->add( + new DateInterval('PT'.$this->webinarSchema->duration.'M') + ); + $this->durationInterval = $now->diff($later); + $this->formattedDuration = $this->durationInterval->format( + $zoomPlugin->get_lang('DurationFormat') + ); + } + } +} diff --git a/plugin/zoom/README.code.md b/plugin/zoom/README.code.md new file mode 100644 index 000000000..43c36e981 --- /dev/null +++ b/plugin/zoom/README.code.md @@ -0,0 +1,70 @@ +The Chamilo Zoom plugin class itself is defined in _plugin/zoom/lib/ZoomPlugin.php_ + +It manipulates both **remote Zoom server objects** and **local database entities**: + +# Local database entities + +The local entities map the remote objects to Chamilo courses/sessions and users. + +They also maintain a cache of the matching remote objects. + +_Entity/*Entity.php_ are the local database entity classes. + +Doctrine entity manager repository classes are in _lib/*EntityRepository.php_. + +# Remote Zoom server objets + +_lib/API/*.php_ contains the Zoom API data structure definition classes, +based on Zoom's own API specification: + +* https://marketplace.zoom.us/docs/api-reference/zoom-api +* https://marketplace.zoom.us/docs/api-reference/zoom-api/Zoom%20API.oas2.json + +These classes provide methods to list, create, update and delete the remote objects. + +# JWT Client + +API class methods use a JWT client implemented in _lib/API/JWTClient.php_. + +The plugin constructor initializes the JWT Client, giving it required API key and secret. + +# Event notification handler + +_endpoint.php_ is the Zoom API event notification web hook end point. + +It handles notifications sent by Zoom servers on useful events : + +* meeting start and end, +* registrant join and leave, +* recordings created and deleted + +# Administrative interface + +_admin.php_ is the administrative interface. +It lists all meetings and recordings. + +# Course tool + +_start.php_ is the **course** tool target: + +* to the course teacher, it shows a course meeting management interface; +* to the course learners, it shows the list of scheduled course meetings. + +# Home page's profile block (also on "My Courses" page) + +This plugin can add 3 kinds of links to "profile block" : + +1. _join_meeting.php?meetingId=…_ links to upcoming meetings accessible to the current user. +_join_meeting.php_ presents the meeting and shows a link to enter the meeting. +2. _user.php_ is the **user**'s own meeting management interface. +3. _global.php_ directs the user to _join_meeting.php_ with the **global** meeting. + +# Meeting management page + +_admin.php_, _start.php_ and _user.php_ link to _meeting.php_. + +_meeting.php_ is the meeting management page, where one can manage + +* the meeting properties, +* the list of its registrants and +* its recordings. \ No newline at end of file diff --git a/plugin/zoom/README.md b/plugin/zoom/README.md new file mode 100644 index 000000000..2e824a74b --- /dev/null +++ b/plugin/zoom/README.md @@ -0,0 +1,148 @@ +This plugin adds Zoom meetings, user registration to meetings and meeting recordings. + +> This plugin requires a Zoom account to manage meetings. + +Once enabled, it will show as an additional course tool in all courses' homepage: teachers will be able to launch a +conference and student to join it. + +## Configuration + +The Zoom API uses JSON Web Tokens (JWT) to authenticate account-level access. JWT apps provide an API Key and Secret +required to authenticate with JWT. To get them, create a JWT App or a Server-to-Sever OAuth app: + +> From June 1, 2023, Zoom recommend that you create a Server-to-Server OAuth application to replace the funcionality of +> a JWT app in your account. + +1. Log into your [Zoom profile page](https://zoom.us/profile) +2. Click on Advanced / Application Marketplace +3. Click on [Develop / Build App](https://marketplace.zoom.us/develop/create) +4. Choose JWT or Server-to-Serve OAuth and then Create +5. Information: Fill in fields about your "App" (application and company names, contact name and email address) +6. Click on Continue +7. App Credentials + 1. For a JWT application: Copy your API Key and Secret to the plugin configuration + 2. For a Server-to-Server OAuth application: Copy your *Account ID*, *Client ID* anf *Client secret* to the plugin + configuration +8. Click on Continue +9. Feature: enable Event Subscriptions to add a new one with endpoint + URL `https://your.chamilo.url/plugin/zoom/endpoint.php` (validate the endpoint to allow to activate the app) and add + these event types: + + - Start Meeting + - End Meeting + - Participant/Host joined meeting + - Participant/Host left meeting + - Start Webinar + - End Webinar + - Participant/Host joined webinar + - Participant/Host left webinar + - All Recordings have completed + - Recording transcript files have completed + + Then click on Done then on Save and copy your *Verification Token* if you have a JWT application or the *Secret + Token* if you have an Server-to-Server OAuth application to the plugin configuration +10. click on Continue +11. Scopes (only for Server-to-Server OAuth application): Click on Add Scopes and select *meeting:write:admin*, + *webinar:write:admin*, *recording:write:admin*. Then click on Done. + +## Changelog + +**v0.5** + +* Added the ability to create a system announcement. + +**v0.4** + +* The creation of webinars is now allowed. +* Added signed attendance to allow you to configure an attendance sheet where participants register their signature. The +signed attendance functionality is similar to that found in the Exercise Signature plugin but does not reuse it. +* Allows you to use multiple accounts and subaccounts to create meetings/webinars + +## Meetings - Webinars + +A **meeting** or **webinar** can be linked to a local **user** and/or a local **course**/**session**: + + * a meeting/webinar with a course is a _course meeting/webinar_; + * a meeting/webinar with a user and no course is a _user meeting/webinar_; + * a meeting/webinar with no course nor user is a _global meeting/webinar_. + +A webinar only can be creadted when your Zoom account has a plan with the webinars feature. + +## Registrants + +A **registrant** is the registration of a local user to a meeting/webinar. + +Users do not register themselves to meetings. + +* They are registered to a course meeting/webinar by the course manager. +* They are registered to a user meeting/webinar by that user. +* They are registered automatically to the global meeting/webinar, when they enter it. + +## Recordings + +A **recording** is the list of files created during a past meeting/webinar instance. + +Course meeting/webinar files can be copied to the course by the course manager. + +# Required Zoom user account + +Recordings and user registration are only available to paying Zoom customers. + +For a non-paying Zoom user, this plugin still works but participants will join anonymously. + +The user that starts the meeting/webinar will be identified as the Zoom account that is defined in the plugin. Socreate +a generic account that works for all the users that start meetings. + +# Databace changelog + +Please, execute this queries in your database: + +**Updatnig to v0.6 from v.0.5** +```sql +UPDATE plugin_zoom_registrant SET type = 'registrant'; + +ALTER TABLE plugin_zoom_registrant + ADD type VARCHAR(255) NOT NULL; +``` + +**Updating to v0.5 from v.0.4** + +```sql +ALTER TABLE plugin_zoom_meeting + ADD sys_announcement_id INT DEFAULT NULL; +ALTER TABLE plugin_zoom_meeting + ADD CONSTRAINT FK_3448092778FB10C FOREIGN KEY (sys_announcement_id) REFERENCES sys_announcement (id) ON DELETE SET NULL; +CREATE INDEX IDX_3448092778FB10C ON plugin_zoom_meeting (sys_announcement_id); +``` + +**Updating to v0.4 from v0.3** + +```sql +ALTER TABLE plugin_zoom_meeting + ADD account_email VARCHAR(255) DEFAULT NULL, + ADD type VARCHAR(255) NOT NULL, + ADD webinar_schema_json LONGTEXT DEFAULT NULL; + +CREATE TABLE plugin_zoom_signature ( + id INT AUTO_INCREMENT NOT NULL, + registrant_id INT DEFAULT NULL, + signature LONGTEXT NOT NULL, + registered_at DATETIME NOT NULL, + UNIQUE INDEX UNIQ_D41895893304A716 (registrant_id), + PRIMARY KEY (id) + ) + DEFAULT CHARACTER SET utf8 + COLLATE `utf8_unicode_ci` + ENGINE = InnoDB; + +ALTER TABLE plugin_zoom_signature + ADD CONSTRAINT FK_D41895893304A716 FOREIGN KEY (registrant_id) REFERENCES plugin_zoom_registrant (id); + +ALTER TABLE plugin_zoom_meeting + ADD sign_attendance TINYINT(1) NOT NULL, + ADD reason_to_sign_attendance LONGTEXT DEFAULT NULL; +``` + +# Contributing + +Read README.code.md for an introduction to the plugin's code. diff --git a/plugin/zoom/activity.php b/plugin/zoom/activity.php new file mode 100644 index 000000000..d7f7df2df --- /dev/null +++ b/plugin/zoom/activity.php @@ -0,0 +1,55 @@ +get_lang('ZoomVideoConferences'); +$meetingId = isset($_REQUEST['meetingId']) ? (int) $_REQUEST['meetingId'] : 0; +if (empty($meetingId)) { + api_not_allowed(true); +} + +$content = ''; +/** @var Meeting $meeting */ +$meeting = $plugin->getMeetingRepository()->findOneBy(['meetingId' => $meetingId]); +if (null === $meeting) { + api_not_allowed(true); +} + +if (!$plugin->userIsConferenceManager($meeting)) { + api_not_allowed(true); +} +$returnURL = 'meetings.php'; +$urlExtra = ''; +if ($meeting->isCourseMeeting()) { + api_protect_course_script(true); + $urlExtra = api_get_cidreq(); + $returnURL = 'start.php?'.$urlExtra; + if (api_is_in_group()) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(), + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.api_get_cidreq(), + 'name' => get_lang('GroupSpace').' '.$meeting->getGroup()->getName(), + ]; + } +} + +$interbreadcrumb[] = [ + 'url' => $returnURL, + 'name' => $plugin->get_lang('ZoomVideoConferences'), +]; +$tpl = new Template($meeting->getMeetingId()); +$tpl->assign('actions', $plugin->getToolbar()); +$tpl->assign('meeting', $meeting); +$tpl->assign('url_extra', $urlExtra); +$tpl->assign('content', $tpl->fetch('zoom/view/activity.tpl')); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/attendance.php b/plugin/zoom/attendance.php new file mode 100644 index 000000000..979546ab0 --- /dev/null +++ b/plugin/zoom/attendance.php @@ -0,0 +1,181 @@ +get('meetingId', 0); + +if (empty($meetingId)) { + api_not_allowed(true); +} + +$plugin = ZoomPlugin::create(); +$em = Database::getManager(); +/** @var Meeting $meeting */ +$meeting = $plugin->getMeetingRepository()->findOneBy(['meetingId' => $meetingId]); +$registrantsRepo = $em->getRepository(Registrant::class); + +if (null === $meeting) { + api_not_allowed( + true, + Display::return_message($plugin->get_lang('MeetingNotFound'), 'error') + ); +} + +if (!$plugin->userIsConferenceManager($meeting) + || !$meeting->isSignAttendance() +) { + api_not_allowed( + true, + Display::return_message(get_lang('NotAvailable'), 'warning') + ); +} + +$getNumberOfSignatures = function () use ($meeting) { + return $meeting->getRegistrants()->count(); +}; + +$getSignaturesData = function ( + $from, + $limit, + $column, + $direction +) use ($registrantsRepo, $meeting) { + if (0 === $column) { + $columnField = 'u.lastname'; + } elseif (1 === $column) { + $columnField = 'u.firstname'; + } else { + $columnField = 's.registeredAt'; + } + + $result = $registrantsRepo->findByMeetingPaginated($meeting, $from, $limit, $columnField, $direction); + + return array_map( + function (Registrant $registrant) { + $signature = $registrant->getSignature(); + + return [ + $registrant->getUser()->getLastname(), + $registrant->getUser()->getFirstname(), + $signature ? $signature->getRegisteredAt() : null, + $signature ? $signature->getFile() : null, + ]; + }, + $result + ); +}; + +if ($httpRequest->query->has('export')) { + $plugin->exportSignatures( + $meeting, + $httpRequest->query->getAlnum('export') + ); +} + +$table = new SortableTable('zoom_signatures', $getNumberOfSignatures, $getSignaturesData, 2); +$table->set_header(0, get_lang('LastName')); +$table->set_header(1, get_lang('FirstName')); +$table->set_header(2, get_lang('DateTime'), true, ['class' => 'text-center'], ['class' => 'text-center']); +$table->set_header(3, $plugin->get_lang('Signature'), false, ['style' => 'width: 200px', 'class' => 'text-center']); +$table->set_additional_parameters( + array_filter( + $httpRequest->query->all(), + function ($key): bool { + return strpos($key, 'zoom_signatures_') === false; + }, + ARRAY_FILTER_USE_KEY + ) +); +$table->set_column_filter( + 2, + function ($dateTime) { + return $dateTime ? api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG) : null; + } +); +$table->set_column_filter( + 3, + function ($imgData) use ($plugin) { + if (empty($imgData)) { + return null; + } + + return Display::img( + $imgData, + $plugin->get_lang('SignatureDone'), + ['class' => 'img-thumbnail'], + false + ); + } +); + +$cidReq = api_get_cidreq(); +$queryParams = 'meetingId='.$meeting->getMeetingId().'&'.$cidReq; +$returnURL = 'meetings.php'; + +if ($meeting->isCourseMeeting()) { + api_protect_course_script(true); + + $this_section = SECTION_COURSES; + + $returnURL = 'start.php?'.$cidReq; + + if (api_is_in_group()) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.$cidReq, + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.$cidReq, + 'name' => get_lang('GroupSpace').' '.$meeting->getGroup()->getName(), + ]; + } +} + +$interbreadcrumb[] = [ + 'url' => $returnURL, + 'name' => $plugin->get_lang('ZoomVideoConferences'), +]; +$interbreadcrumb[] = [ + 'url' => 'meeting.php?'.$queryParams, + 'name' => $meeting->getMeetingInfoGet()->topic, +]; + +$exportPdfLink = Display::url( + Display::return_icon('pdf.png', get_lang('ExportToPDF'), [], ICON_SIZE_MEDIUM), + api_get_self().'?'.$queryParams.'&export=pdf' +); +$exportXlsLink = Display::url( + Display::return_icon('excel.png', get_lang('ExportAsXLS'), [], ICON_SIZE_MEDIUM), + api_get_self().'?'.$queryParams.'&export=xls' +); + +$pageTitle = $plugin->get_lang('Attendance'); + +$content = ' +
    +
    '.$plugin->get_lang('ReasonToSign').'
    +
    '.$meeting->getReasonToSignAttendance().'
    +
    + '.$table->return_table(); + +$tpl = new Template($pageTitle); +$tpl->assign( + 'actions', + Display::toolbarAction( + 'attendance-actions', + [$exportPdfLink.PHP_EOL.$exportXlsLink] + ) +); +$tpl->assign('header', $pageTitle); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/calendar.ajax.php b/plugin/zoom/calendar.ajax.php new file mode 100644 index 000000000..4b2b2a471 --- /dev/null +++ b/plugin/zoom/calendar.ajax.php @@ -0,0 +1,66 @@ +get('a'); + +if ($action == 'get_events') { + $startDate = $request->query->get('start'); + $endDate = $request->query->get('end'); + + $startDate = api_get_utc_datetime($startDate, true, true); + $endDate = api_get_utc_datetime($endDate, true, true); + + $meetings = $plugin + ->getMeetingRepository() + ->periodMeetings($startDate, $endDate); + + $meetingsAsEvents = array_map( + function (Meeting $conference) { + $isWebinar = $conference instanceof Webinar; + /** @var BaseMeetingTrait $schema */ + $schema = $isWebinar ? $conference->getWebinarSchema() : $conference->getMeetingInfoGet(); + + $endDate = new DateTime($conference->formattedStartTime); + $endDate->add($conference->durationInterval); + + return [ + 'id' => 'meeting_'.$conference->getId(), + 'title' => $schema->topic, + 'typeName' => $conference->typeName, + 'editable' => false, + 'start' => $conference->formattedStartTime, + 'start_date_localtime' => $conference->formattedStartTime, + 'end' => $endDate->format('Y-m-d H:i'), + 'end_date_localtime' => $endDate->format('Y-m-d H:i'), + 'duration' => $conference->formattedDuration, + 'description' => $schema->agenda, + 'allDay' => false, + 'accountEmail' => $conference->getAccountEmail(), + ]; + }, + $meetings + ); + + $response = JsonResponse::create($meetingsAsEvents); + $response->send(); +} diff --git a/plugin/zoom/calendar.php b/plugin/zoom/calendar.php new file mode 100644 index 000000000..00322ef6e --- /dev/null +++ b/plugin/zoom/calendar.php @@ -0,0 +1,47 @@ +get_lang('ZoomVideoConferences'); + +$defaultView = api_get_setting('default_calendar_view'); + +if (empty($defaultView)) { + $defaultView = 'month'; +} + +$regionValue = api_get_language_isocode(); + +$htmlHeadXtra[] = api_get_asset('qtip2/jquery.qtip.min.js'); +$htmlHeadXtra[] = api_get_asset('fullcalendar/dist/fullcalendar.js'); +$htmlHeadXtra[] = api_get_asset('fullcalendar/dist/locale-all.js'); +$htmlHeadXtra[] = api_get_css_asset('fullcalendar/dist/fullcalendar.min.css'); +$htmlHeadXtra[] = api_get_css_asset('qtip2/jquery.qtip.min.css'); + +$tpl = new Template($toolName); + +$tpl->assign('web_agenda_ajax_url', 'calendar.ajax.php?sec_token='.Security::get_token()); +$tpl->assign('default_view', $defaultView); +$tpl->assign('region_value', 'en' === $regionValue ? 'en-GB' : $regionValue); + +$onHoverInfo = Agenda::returnOnHoverInfo(); +$tpl->assign('on_hover_info', $onHoverInfo); + +$extraSettings = Agenda::returnFullCalendarExtraSettings(); + +$tpl->assign('fullcalendar_settings', $extraSettings); + +$content = $tpl->fetch('zoom/view/calendar.tpl'); + +$tpl->assign('actions', $plugin->getToolbar()); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/config.php b/plugin/zoom/config.php new file mode 100644 index 000000000..92a1ce705 --- /dev/null +++ b/plugin/zoom/config.php @@ -0,0 +1,5 @@ +isMethod('POST')) { + http_response_code(Response::HTTP_NOT_FOUND); + exit; +} + +$configAccountId = api_get_plugin_setting('zoom', ZoomPlugin::SETTING_ACCOUNT_ID); +$configClientId = api_get_plugin_setting('zoom', ZoomPlugin::SETTING_CLIENT_ID); +$configClientSecret = api_get_plugin_setting('zoom', ZoomPlugin::SETTING_CLIENT_SECRET); +$configSecretToken = api_get_plugin_setting('zoom', ZoomPlugin::SETTING_SECRET_TOKEN); + +$isS2SApp = !empty($configAccountId) && !empty($configClientId) && !empty($configClientSecret); +$isJwtApp = !$isS2SApp; + +$authorizationHeaderValue = $request->headers->get('Authorization'); + +if ($isJwtApp && api_get_plugin_setting('zoom', 'verificationToken') !== $authorizationHeaderValue) { + error_log('verificationToken not valid, please check your zoom configuration'); + http_response_code(Response::HTTP_UNAUTHORIZED); + exit; +} + +$body = file_get_contents('php://input'); +$decoded = json_decode($body); + +if ('endpoint.url_validation' === $decoded->event) { + $json = json_encode([ + 'plainToken' => $decoded->payload->plainToken, + 'encryptedToken' => hash_hmac('sha256', $decoded->payload->plainToken, $configSecretToken), + ]); + + echo $json; + exit(); +} + +if (is_null($decoded) || !is_object($decoded) || !isset($decoded->event) || !isset($decoded->payload->object)) { + error_log(sprintf('Did not recognize event notification: %s', $body)); + http_response_code(Response::HTTP_UNPROCESSABLE_ENTITY); + exit; +} + +$object = $decoded->payload->object; +list($objectType, $action) = explode('.', $decoded->event); + +$em = Database::getManager(); + +$meetingRepository = $em->getRepository(Meeting::class); +$meeting = null; +if ($object->id) { + /** @var Meeting $meeting */ + $meeting = $meetingRepository->findOneBy(['meetingId' => $object->id]); +} + +if (null === $meeting) { + error_log("Meeting not found"); + error_log(sprintf('Event "%s" on %s was unhandled: %s', $action, $objectType, $body)); + http_response_code(Response::HTTP_NOT_FOUND); + exit; +} + +$activity = new MeetingActivity(); +$activity->setName($action); +$activity->setType($objectType); +$activity->setEvent(json_encode($object)); + +switch ($objectType) { + case 'meeting': + error_log('Meeting '.$action.' - '.$meeting->getId()); + error_log(print_r($object, 1)); + + switch ($action) { + case 'deleted': + $em->remove($meeting); + break; + case 'ended': + case 'started': + $meeting->setStatus($action); + $meeting->addActivity($activity); + $em->persist($meeting); + break; + default: + $meeting->addActivity($activity); + $em->persist($meeting); + break; + } + $em->flush(); + break; + case 'webinar': + $meeting->addActivity($activity); + $em->persist($meeting); + $em->flush(); + break; + case 'recording': + $recordingRepository = $em->getRepository(Recording::class); + + $recordingEntity = null; + if ($object->uuid) { + /** @var Recording $recordingEntity */ + $recordingEntity = $recordingRepository->findOneBy(['uuid' => $object->uuid, 'meeting' => $meeting]); + } + + error_log("Recording: $action"); + error_log(print_r($object, 1)); + + switch ($action) { + case 'completed': + $recording = new Recording(); + $recording->setRecordingMeeting(RecordingMeeting::fromObject($object)); + $recording->setMeeting($meeting); + $meeting->addActivity($activity); + $em->persist($meeting); + $em->persist($recording); + $em->flush(); + break; + case 'recovered': + /*if (null === $recordingEntity) { + $em->persist( + (new Recording())->setRecordingMeeting(RecordingMeeting::fromObject($object)) + ); + $em->flush(); + }*/ + break; + case 'trashed': + case 'deleted': + $meeting->addActivity($activity); + if (null !== $recordingEntity) { + $recordMeeting = $recordingEntity->getRecordingMeeting(); + $recordingToDelete = RecordingMeeting::fromObject($object); + $files = []; + if ($recordingToDelete->recording_files) { + foreach ($recordingToDelete->recording_files as $fileToDelete) { + foreach ($recordMeeting->recording_files as $file) { + if ($fileToDelete->id != $file->id) { + $files[] = $file; + } + } + } + } + + if (empty($files)) { + $em->remove($recordingEntity); + } else { + $recordMeeting->recording_files = $files; + $recordingEntity->setRecordingMeeting($recordMeeting); + $em->persist($recordingEntity); + } + } + $em->persist($meeting); + $em->flush(); + break; + default: + $meeting->addActivity($activity); + $em->persist($meeting); + $em->flush(); + break; + } + break; + default: + error_log(sprintf('Event "%s" on %s was unhandled: %s', $action, $objectType, $body)); + http_response_code(501); // Not Implemented + break; +} diff --git a/plugin/zoom/global.php b/plugin/zoom/global.php new file mode 100644 index 000000000..7105b8f2f --- /dev/null +++ b/plugin/zoom/global.php @@ -0,0 +1,13 @@ +getGlobalMeeting()->getMeetingId()); diff --git a/plugin/zoom/index.php b/plugin/zoom/index.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/plugin/zoom/index.php @@ -0,0 +1 @@ +install(); diff --git a/plugin/zoom/join_meeting.php b/plugin/zoom/join_meeting.php new file mode 100644 index 000000000..241f5054d --- /dev/null +++ b/plugin/zoom/join_meeting.php @@ -0,0 +1,114 @@ +getMeetingRepository()->findOneBy(['meetingId' => $meetingId]); +if (null === $meeting) { + api_not_allowed(true, $plugin->get_lang('MeetingNotFound')); +} + +if ($meeting->isCourseMeeting()) { + api_protect_course_script(true); + if (api_is_in_group()) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(), + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.api_get_cidreq(), + 'name' => get_lang('GroupSpace').' '.$meeting->getGroup()->getName(), + ]; + } +} + +$startJoinURL = ''; +$detailsURL = ''; +$signature = ''; +$btnAnnouncement = ''; + +$currentUser = api_get_user_entity(api_get_user_id()); +$isConferenceManager = $plugin->userIsConferenceManager($meeting); + +try { + $startJoinURL = $plugin->getStartOrJoinMeetingURL($meeting); + + if (empty($startJoinURL)) { + Display::addFlash( + Display::return_message($plugin->get_lang('ConferenceNotAvailable'), 'warning') + ); + } + + if ($meeting->isSignAttendance() && !$isConferenceManager) { + $registrant = $meeting->getRegistrantByUser($currentUser); + $signature = $registrant ? $registrant->getSignature() : null; + + Security::get_token('zoom_signature'); + } + + if ($isConferenceManager) { + $detailsURL = api_get_path(WEB_PLUGIN_PATH).'zoom/meeting.php?meetingId='.$meeting->getMeetingId(); + } + + $allowAnnouncementsToSessionAdmin = api_get_configuration_value('session_admin_access_system_announcement'); + + if (api_is_platform_admin($allowAnnouncementsToSessionAdmin)) { + $announcementUrl = ''; + + if ($announcement = $meeting->getSysAnnouncement()) { + $announcementUrl = api_get_path(WEB_CODE_PATH).'admin/system_announcements.php?' + .http_build_query( + [ + 'action' => 'edit', + 'id' => $announcement->getId(), + ] + ); + } else { + $announcementUrl = api_get_path(WEB_CODE_PATH).'admin/system_announcements.php?' + .http_build_query( + [ + 'action' => 'add', + 'type' => 'zoom_conference', + 'meeting' => $meeting->getMeetingId(), + ] + ); + } + + $btnAnnouncement = Display::toolbarButton( + $announcement ? get_lang('EditSystemAnnouncement') : get_lang('AddSystemAnnouncement'), + $announcementUrl, + 'bullhorn' + ); + } +} catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'warning') + ); +} + +$htmlHeadXtra[] = api_get_asset('signature_pad/signature_pad.umd.js'); + +$tpl = new Template($meeting->getMeetingId()); +$tpl->assign('meeting', $meeting); +$tpl->assign('start_url', $startJoinURL); +$tpl->assign('details_url', $detailsURL); +$tpl->assign('btn_announcement', $btnAnnouncement); +$tpl->assign('is_conference_manager', $isConferenceManager); +$tpl->assign('signature', $signature); +$content = $tpl->fetch('zoom/view/join.tpl'); +$tpl->assign('actions', $plugin->getToolbar()); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/lang/english.php b/plugin/zoom/lang/english.php new file mode 100644 index 000000000..5cfe0af28 --- /dev/null +++ b/plugin/zoom/lang/english.php @@ -0,0 +1,191 @@ +this app type will be deprecated on 6/1/2023)'; +$strings['apiSecret'] = 'API Secret'; +$strings['apiSecret_help'] = 'For a JWT application type (this app type will be deprecated on 6/1/2023)'; +$strings['verificationToken'] = 'Verification Token'; +$strings['verificationToken_help'] = 'For a JWT application type (this app type will be deprecated on 6/1/2023)'; +$strings[ZoomPlugin::SETTING_ACCOUNT_ID] = 'Account ID'; +$strings[ZoomPlugin::SETTING_ACCOUNT_ID.'_help'] = 'For a Server-to-Server OAuth application type'; +$strings[ZoomPlugin::SETTING_CLIENT_ID] = 'Client ID'; +$strings[ZoomPlugin::SETTING_CLIENT_ID.'_help'] = 'For a Server-to-Server OAuth application type'; +$strings[ZoomPlugin::SETTING_CLIENT_SECRET] = 'Client secret'; +$strings[ZoomPlugin::SETTING_CLIENT_SECRET.'_help'] = 'For a Server-to-Server OAuth application type'; +$strings[ZoomPlugin::SETTING_SECRET_TOKEN] = 'Secret token'; +$strings[ZoomPlugin::SETTING_SECRET_TOKEN.'_help'] = 'For a Server-to-Server OAuth application type'; +$strings['enableParticipantRegistration'] = 'Enable participant registration'; +$strings['enablePresenter'] = 'Enable presenter'; +$strings['enablePresenter_help'] = 'It requires that Enable participant registration settings is enabled.'; +$strings['enableCloudRecording'] = 'Automatic recording type'; +$strings['enableGlobalConference'] = 'Enable global conference'; +$strings['enableGlobalConferencePerUser'] = 'Enable global conference per user'; +$strings['globalConferenceAllowRoles'] = "Global conference link only visible for these user roles"; +$strings['globalConferencePerUserAllowRoles'] = "Global conference per user link only visible for these user roles"; +$strings['accountSelector'] = 'Account selector'; +$strings['accountSelector_help'] = 'It allows you to declare the emails of the different accounts with whom you want to open the Zoom videos. Separated by semicolons (account_one@example.come;account_two@exaple.com).'; + +$strings['tool_enable_help'] = "Choose whether you want to enable the Zoom videoconference tool. +Once enabled, it will show as an additional course tool in all courses' homepage : +teachers will be able to launch a conference and student to join it. +
    +This plugin requires a Zoom account to manage meetings. +

    The Zoom API uses JSON Web Tokens (JWT) to authenticate account-level access. To get them, create a JWT App or a Server-to-Sever OAuth app:

    +
    +

    From June 1, 2023, Zoom recommend that you create a Server-to-Server OAuth application to replace the funcionality of + a JWT app in your account.

    +
    +
      +
    1. Log into your Zoom profile page
    2. +
    3. Click on Advanced / Application Marketplace
    4. +
    5. Click on Develop / Build App
    6. +
    7. Choose JWT or Server-to-Serve OAuth and then Create
    8. +
    9. Information: Fill in fields about your \"App\" (application and company names, contact name and email address)
    10. +
    11. Click on Continue
    12. +
    13. App Credentials +
        +
      1. For a JWT application: Copy your API Key and Secret to the plugin configuration
      2. +
      3. For a Server-to-Server OAuth application: Copy your Account ID, Client ID and Client secret to the plugin +configuration
      4. +
    14. +
    15. Click on Continue
    16. +
    17. Feature: enable Event Subscriptions to add a new one with endpoint URL +https://your.chamilo.url/plugin/zoom/endpoint.php (validate the endpoint to allow to activate the app) and add +these event types:

      +
        +
      • Start Meeting
      • +
      • End Meeting
      • +
      • Participant/Host joined meeting
      • +
      • Participant/Host left meeting
      • +
      • Start Webinar
      • +
      • End Webinar
      • +
      • Participant/Host joined webinar
      • +
      • Participant/Host left webinar
      • +
      • All Recordings have completed
      • +
      • Recording transcript files have completed
      • +
      +

      Then click on Done then on Save and copy your Verification Token if you have a JWT application or the Secret +Token if you have an Server-to-Server OAuth application to the plugin configuration

    18. +
    19. click on Continue
    20. +
    21. Scopes (only for Server-to-Server OAuth application): Click on Add Scopes and select meeting:write:admin, +webinar:write:admin, recording:write:admin. Then click on Done.
    22. +
    +
    +Attention: +
    Zoom is NOT free software and specific rules apply to personal data protection. +Please check with Zoom and make sure they satisfy you and learning users."; + +$strings['enableParticipantRegistration_help'] = "Requires a paying Zoom profile. +Will not work for a basic profile."; + +$strings['enableCloudRecording_help'] = "Requires a paying Zoom profile. +Will not work for a basic profile."; + +// please keep these lines alphabetically sorted +$strings['AllCourseUsersWereRegistered'] = "All course students were registered"; +$strings['Agenda'] = "Agenda"; +$strings['CannotRegisterWithoutEmailAddress'] = "Cannot register without email address"; +$strings['CopyingJoinURL'] = "Copying join URL"; +$strings['CopyJoinAsURL'] = "Copy 'join as' URL"; +$strings['CopyToCourse'] = "Copy to course"; +$strings['CouldNotCopyJoinURL'] = "Could not copy join URL"; +$strings['Course'] = "Cours"; +$strings['CreatedAt'] = "Created at"; +$strings['CreateLinkInCourse'] = "Create link(s) in course"; +$strings['CreateUserVideoConference'] = "Create user conference"; +$strings['DateMeetingTitle'] = "%s: %s"; +$strings['DeleteMeeting'] = "Delete meeting"; +$strings['DeleteFile'] = "Delete file(s)"; +$strings['Details'] = "Details"; +$strings['DoIt'] = "Do it"; +$strings['Duration'] = "Duration"; +$strings['DurationFormat'] = "%hh%I"; +$strings['DurationInMinutes'] = "Duration (in minutes)"; +$strings['EndDate'] = "End Date"; +$strings['EnterMeeting'] = "Enter meeting"; +$strings['ViewMeeting'] = "View meeting"; +$strings['Files'] = "Files"; +$strings['Finished'] = "finished"; +$strings['FileWasCopiedToCourse'] = "The file was copied to the course"; +$strings['FileWasDeleted'] = "The file was deleted"; +$strings['GlobalMeeting'] = "Global conference"; +$strings['GlobalMeetingPerUser'] = "Global conference per user"; +$strings['GroupUsersWereRegistered'] = "Group members were registered"; +$strings['InstantMeeting'] = "Instant meeting"; +$strings['Join'] = "Join"; +$strings['JoinGlobalVideoConference'] = "Join global conference"; +$strings['JoinURLCopied'] = "Join URL copied"; +$strings['JoinURLToSendToParticipants'] = "Join URL to send to participants"; +$strings['LiveMeetings'] = "Live meetings"; +$strings['LinkToFileWasCreatedInCourse'] = "A link to the file was added to the course"; +$strings['MeetingDeleted'] = "Meeting deleted"; +$strings['MeetingsFound'] = "Meetings found"; +$strings['MeetingUpdated'] = "Meeting updated"; +$strings['NewMeetingCreated'] = "New meeting created"; +$strings['Password'] = "Password"; +$strings['RecurringWithFixedTime'] = "Recurring with fixed time"; +$strings['RecurringWithNoFixedTime'] = "Recurring with no fixed time"; +$strings['RegisterAllCourseUsers'] = "Register all course users"; +$strings['RegisteredUserListWasUpdated'] = "Registered user list updated"; +$strings['RegisteredUsers'] = "Registered users"; +$strings['RegisteredPresenters'] = "Registered presenters"; +$strings['RegisterNoUser'] = "Register no user"; +$strings['RegisterTheseGroupMembers'] = "Register these group members"; +$strings['ScheduleAMeeting'] = "Schedule a meeting"; +$strings['ScheduledMeeting'] = "Scheduled meeting"; +$strings['ScheduledMeetings'] = "Scheduled Meetings"; +$strings['ScheduleAMeeting'] = "Schedule a meeting"; +$strings['SearchMeeting'] = "Search meeting"; +$strings['Session'] = "Session"; +$strings['StartDate'] = "Start Date"; +$strings['Started'] = "started"; +$strings['StartInstantMeeting'] = "Start instant meeting"; +$strings['StartMeeting'] = "Start meeting"; +$strings['StartTime'] = "Start time"; +$strings['Topic'] = "Topic"; +$strings['TopicAndAgenda'] = "Topic and agenda"; +$strings['Type'] = "Type"; +$strings['UpcomingMeetings'] = "Upcoming meetings"; +$strings['UpdateMeeting'] = "Update meeting"; +$strings['UpdateRegisteredUserList'] = "Update registered user list"; +$strings['UserRegistration'] = "User registration"; +$strings['Y-m-d H:i'] = "Y-m-d H:i"; +$strings['Waiting'] = "waiting"; +$strings['XRecordingOfMeetingXFromXDurationXDotX'] = "%s recording of meeting %s from %s (%s).%s"; +$strings['YouAreNotRegisteredToThisMeeting'] = "You are not registered to this meeting"; +$strings['ZoomVideoConferences'] = "Zoom Video Conferences"; +$strings['Recordings'] = "Recordings"; +$strings['CreateGlobalVideoConference'] = "Create global video conference"; +$strings['ConferenceNotStarted'] = "Conference not started"; +$strings['MeetingNotFound'] = "Meeting not found"; +$strings['JoinURLNotAvailable'] = "URL not available"; +$strings['Meetings'] = "Meetings"; +$strings['ConferenceType'] = "Conference type"; +$strings['ForEveryone'] = "Everyone"; +$strings['SomeUsers'] = "Some users (Select later)"; +$strings['Activity'] = "Activity"; +$strings['ConferenceNotAvailable'] = "Conference not available"; +$strings['SignAttendance'] = "Sign attendance"; +$strings['ReasonToSign'] = 'Reason to sign attendance'; +$strings['ConferenceWithAttendance'] = "Conference with attendance sign"; +$strings['Sign'] = "Sign"; +$strings['Signature'] = "Signature"; +$strings['Meeting'] = "Meeting"; +$strings['Webinar'] = "Webinar"; +$strings['AudienceType'] = 'Audience type'; +$strings['AccountEmail'] = 'Account email'; +$strings['NewWebinarCreated'] = "New webinar created"; +$strings['UpdateWebinar'] = 'Update webinar'; +$strings['WebinarUpdated'] = "Webinar updated"; +$strings['DeleteWebinar'] = "Delete webinar"; +$strings['WebinarDeleted'] = "Webinar deleted"; +$strings['UrlForSelfRegistration'] = "URL for self registration"; +$strings['RegisterMeToConference'] = "Register me to conference"; +$strings['UnregisterMeToConference'] = "Unregister me to conference"; +$strings['Presenters'] = "Presenters"; diff --git a/plugin/zoom/lang/french.php b/plugin/zoom/lang/french.php new file mode 100644 index 000000000..b6a9bb8c5 --- /dev/null +++ b/plugin/zoom/lang/french.php @@ -0,0 +1,173 @@ +API Key)"; +$strings['apiSecret'] = "Code secret d'API (API Secret)"; +$strings['enableParticipantRegistration'] = "Activer l'inscription des participants"; +$strings['enableCloudRecording'] = "Type d'enregistrement automatique"; +$strings['enableGlobalConference'] = "Activer les conférences globales"; +$strings['enableGlobalConferencePerUser'] = "Activer les conférences globales par utilisateur"; +$strings['globalConferenceAllowRoles'] = "Visibilité du lien de vidéo conférence global pour les profils suivant"; +$strings['globalConferencePerUserAllowRoles'] = "Visibilité du lien de vidéo conférence global par utilisateur pour les profils suivant"; +$strings['tool_enable_help'] = "Choisissez si vous voulez activer l'outil de conférence vidéo Zoom. +Une fois activé, il apparaitra dans les pages d'accueil de tous les cours : +les enseignants pourront démarrer une conférence et les étudiants la rejoindre. +
    +Ce plugin requiert un compte Zoom pour gérer les conférences. +

    L'API de Zoom utilise les JSON Web Tokens (JWT) pour autoriser l'accès à un compte. +Pour les obtenir, créez une application JWT ou une application OAuth serveur à serveur :

    +
    +

    À partir du 1er juin 2023, Zoom recommande de créer une application OAuth serveur à serveur +pour remplacer la fonctionnalité d'une application JWT dans votre compte.

    +
    +
      +
    1. Connectez-vous à votre page de profil Zoom
    2. +
    3. Cliquez sur Avancé / Marketplace d'application
    4. +
    5. Cliquez sur Développer / Créer une application
    6. +
    7. Choisissez JWT ou OAuth serveur à serveur, puis Créer
    8. +
    9. Informations : Remplissez les champs sur votre \"App\" (noms de l'application et de la société, nom et adresse e-mail de contact)
    10. +
    11. Cliquez sur Continuer
    12. +
    13. Identifiants de l'application +
        +
      1. Pour une application JWT : Copiez votre clé API (API Key) et votre code secret (API Secret) dans la configuration du plugin
      2. +
      3. Pour une application OAuth serveur à serveur : Copiez votre ID de compte, votre ID de client et votre secret de client dans la configuration du plugin
      4. +
    14. +
    15. Cliquez sur Continuer
    16. +
    17. Fonctionnalité : activez les Abonnements aux événements / Event Subscriptions pour en ajouter un nouveau avec comme endpoint URL +https://your.chamilo.url/plugin/zoom/endpoint.php (validez le point de terminaison pour permettre l'activation de l'application) et ajoutez +ces types d'événements :

      +
        +
      • Démarrer une réunion / Start Meeting
      • +
      • Terminer une réunion / End Meeting
      • +
      • Participant/hôte a rejoint la réunion / Participant/Host joined meeting
      • +
      • Participant/hôte a quitté la réunion / Participant/Host left meeting
      • +
      • Démarrer un webinar / Start Webinar
      • +
      • Terminer un webinar / End Webinar
      • +
      • Participant/hôte a rejoint le webinar / Participant/Host joined webinar
      • +
      • Participant/hôte a quitté le webinar / Participant/Host left webinar
      • +
      • Toutes les enregistrements sont terminées / All Recordings have completed
      • +
      • Les fichiers de transcription d'enregistrement sont terminés / Recording transcript files have completed
      • +
      +

      Ensuite, cliquez sur Terminé, puis sur Enregistrer et copiez votre Jeton de vérification / Verification Token si vous avez une application JWT ou le Jeton Secret / Secret +Token si vous avez une application OAuth serveur à serveur dans la configuration du plugin

    18. +
    19. cliquez sur Continuer
    20. +
    21. Scopes (uniquement pour l'application OAuth serveur à serveur) : cliquez sur Ajouter des scopes / Add Scopes et sélectionnez meeting:write:admin, webinar:write:admin, recording:write:admin. Puis cliquez sur Terminé.
    22. +
    +
    +Attention : +
    Zoom n'est PAS un logiciel libre +et des règles spécifiques de protection des données personnelles s'y appliquent. +Merci de vérifier auprès de Zoom qu'elles sont satisfaisantes pour vous et les apprenants qui l'utiliseront."; + +$strings['enableParticipantRegistration_help'] = "Nécessite un profil Zoom payant. +Ne fonctionnera pas pour un profil de base."; + +$strings['enableCloudRecording_help'] = "Nécessite un profil Zoom payant. +Ne fonctionnera pas pour un profil de base."; + +// please keep these lines alphabetically sorted +$strings['AllCourseUsersWereRegistered'] = "Tous les étudiants du cours sont inscrits"; +$strings['Agenda'] = "Ordre du jour"; +$strings['CannotRegisterWithoutEmailAddress'] = "Impossible d'inscrire un utilisateur sans adresse de courriel"; +$strings['CopyingJoinURL'] = "Copie de l'URL pour rejoindre en cours"; +$strings['CopyJoinAsURL'] = "Copier l'URL pour 'rejoindre en tant que'"; +$strings['CopyToCourse'] = "Copier dans le cours"; +$strings['CouldNotCopyJoinURL'] = "Échec de la copie de l'URL pour rejoindre"; +$strings['Course'] = "Cours"; +$strings['CreatedAt'] = "Créé à"; +$strings['CreateLinkInCourse'] = "Créer dans le cours un ou des lien(s) vers le(s) fichier(s)"; +$strings['CreateUserVideoConference'] = "Créer des conferences par utilisateur"; +$strings['DateMeetingTitle'] = "%s : %s"; +$strings['DeleteMeeting'] = "Effacer la conférence"; +$strings['DeleteFile'] = "Supprimer ce(s) fichier(s)"; +$strings['Details'] = "Détail"; +$strings['DoIt'] = "Fais-le"; +$strings['Duration'] = "Durée"; +$strings['DurationFormat'] = "%hh%I"; +$strings['DurationInMinutes'] = "Durée (en minutes)"; +$strings['EndDate'] = "Date de fin"; +$strings['EnterMeeting'] = "Entrer dans la conférence"; +$strings['ViewMeeting'] = "Voir la conférence"; +$strings['Files'] = "Fichiers"; +$strings['Finished'] = "terminée"; +$strings['FileWasCopiedToCourse'] = "Le fichier a été copié dans le cours"; +$strings['FileWasDeleted'] = "Le fichier a été effacé"; +$strings['GlobalMeeting'] = "Conférence globale"; +$strings['GroupUsersWereRegistered'] = "Les membres des groupes ont été inscrits"; +$strings['InstantMeeting'] = "Conférence instantanée"; +$strings['Join'] = "Rejoindre"; +$strings['JoinGlobalVideoConference'] = "Rejoindre la conférence globale"; +$strings['JoinURLCopied'] = "URL pour rejoindre copiée"; +$strings['JoinURLToSendToParticipants'] = "URL pour assister à la conférence (à envoyer aux participants)"; +$strings['LiveMeetings'] = "Conférences en cours"; +$strings['LinkToFileWasCreatedInCourse'] = "Un lien vers le fichier a été ajouter au cours"; +$strings['MeetingDeleted'] = "Conférence effacée"; +$strings['MeetingsFound'] = "Conférences trouvées"; +$strings['MeetingUpdated'] = "Conférence mise à jour"; +$strings['NewMeetingCreated'] = "Nouvelle conférence créée"; +$strings['Password'] = "Mot de passe"; +$strings['RecurringWithFixedTime'] = "Recurrent, à heure fixe"; +$strings['RecurringWithNoFixedTime'] = "Recurrent, sans heure fixe"; +$strings['RegisterAllCourseUsers'] = "Inscrire tous les utilisateurs du cours"; +$strings['RegisteredUserListWasUpdated'] = "Liste des utilisateurs inscrits mise à jour"; +$strings['RegisteredUsers'] = "Utilisateurs inscrits"; +$strings['RegisterNoUser'] = "N'inscrire aucun utilisateur"; +$strings['RegisterTheseGroupMembers'] = "Inscrire les membres de ces groupes"; +$strings['ScheduleAMeeting'] = "Programmer une conférence"; +$strings['ScheduledMeeting'] = "Conférence programmée"; +$strings['ScheduledMeetings'] = "Conférences programmées"; +$strings['ScheduleAMeeting'] = "Programmer une conférence"; +$strings['SearchMeeting'] = "Rechercher une conférence"; +$strings['Session'] = "Session"; +$strings['StartDate'] = "Date de début"; +$strings['Started'] = "démarrée"; +$strings['StartInstantMeeting'] = "Démarrer une conférence instantanée"; +$strings['StartMeeting'] = "Démarrer la conférence"; +$strings['StartTime'] = "Heure de début"; +$strings['Topic'] = "Objet"; +$strings['TopicAndAgenda'] = "Objet et ordre du jour"; +$strings['Type'] = "Type"; +$strings['UpcomingMeeting'] = "Conférences à venir"; +$strings['UpdateMeeting'] = "Mettre à jour la conférence"; +$strings['UpdateRegisteredUserList'] = "Mettre à jour la liste des utilisateurs inscrits"; +$strings['UserRegistration'] = "Inscription des utilisateurs"; +$strings['Y-m-d H:i'] = "d/m/Y à H\hi"; +$strings['verificationToken'] = 'Verification Token'; +$strings['Waiting'] = "en attente"; +$strings['XRecordingOfMeetingXFromXDurationXDotX'] = "Enregistrement (%s) de la conférence %s de %s (%s).%s"; +$strings['YouAreNotRegisteredToThisMeeting'] = "Vous n'êtes pas inscrit à cette conférence"; +$strings['ZoomVideoConferences'] = "Conférences vidéo Zoom"; +$strings['Recordings'] = "Enregistrements"; +$strings['CreateGlobalVideoConference'] = "Créer une conférence global"; +$strings['JoinURLNotAvailable'] = "URL pas disponible"; +$strings['Meetings'] = "Conférences"; +$strings['Activity'] = "Activité"; +$strings['ConferenceNotAvailable'] = "Conférence non disponible"; +$strings['SignAttendance'] = "Signer la feuille d'émargement"; +$strings['ReasonToSign'] = "Explication pour signer la feuille d'émargement"; +$strings['ConferenceWithAttendance'] = "Conférence avec signature d'émargement"; +$strings['Sign'] = "Signer"; +$strings['Signature'] = "Signature"; +$strings['Meeting'] = "Meeting"; +$strings['Webinar'] = "Webinar"; +$strings['AudienceType'] = "Type d'audience"; +$strings['AccountEmail'] = "Compte email"; +$strings['NewWebinarCreated'] = "Nouveau webinar créé"; +$strings['UpdateWebinar'] = "Mettre à jour le webinar"; +$strings['WebinarUpdated'] = "Webinar mis à jour"; +$strings['DeleteWebinar'] = "Supprimer le webinar"; +$strings['WebinarDeleted'] = "Webinar supprimé"; +$strings['UrlForSelfRegistration'] = "URL pour l'auto-inscription des participants"; +$strings['RegisterMeToConference'] = "M'inscrire à la visio"; +$strings['UnregisterMeToConference'] = "Me désinscrire de la visio"; +$strings['ForEveryone'] = "Tout le monde"; +$strings['SomeUsers'] = "Utilisateurs inscrits (à inscrire après)"; +$strings['Presenters'] = "Animateurs"; +$strings['RegisteredPresenters'] = "Animateurs enregistrés"; +$strings['enablePresenter_help'] = "Il est nécessaire que l'option Activer l'inscription des participants soit sur Oui."; +$strings['enablePresenter'] = 'Activer les animateurs'; diff --git a/plugin/zoom/lang/spanish.php b/plugin/zoom/lang/spanish.php new file mode 100644 index 000000000..c5995e80f --- /dev/null +++ b/plugin/zoom/lang/spanish.php @@ -0,0 +1,174 @@ +API Key)"; +$strings['apiSecret'] = "Código secreto de API (API Secret)"; +$strings['enableParticipantRegistration'] = "Activar la inscripción de participantes"; +$strings['enablePresenter'] = 'Activar presentadores'; +$strings['enablePresenter_help'] = 'Requiere que el parametro Activar la inscripción de participantes este activado.'; +$strings['enableCloudRecording'] = "Tipo de grabación automática"; +$strings['enableGlobalConference'] = "Activar las conferencias globales"; +$strings['enableGlobalConferencePerUser'] = "Activar las conferencias globales por usuario"; +$strings['globalConferenceAllowRoles'] = "Visibilidad del enlace global de videoconferencia para los perfiles siguientes"; +$strings['globalConferencePerUserAllowRoles'] = "Visibilidad del enlace global de videoconferencia por usuario para los perfiles siguientes"; +$strings['accountSelector'] = 'Selector de cuentas'; +$strings['accountSelector_help'] = 'Te permite declarar los correos de las diferentes cuentas con las que quieres abrir los videos de Zoom. Separados por punto y coma (account_one@example.come;account_two@exaple.com).'; + +$strings['tool_enable_help'] = "Escoja si desea activar la herramienta Zoom. +Una vez activada, aparecerá en las páginas principales de todos los cursos. Los profesores podrán +iniciar una conferencia y los alumnos juntarse a ella. +
    +Este plugin requiere una cuenta Zoom para gestionar las conferencias. +

    El API de Zoom utiliza los JSON Web Tokens (JWT) para autorizar el acceso a una cuenta. +Para obtenerlos, crea una app JWT o una aplicación Server-to-Server OAuth:

    +
    +

    Desde el 1 de junio de 2023, Zoom recomienda que usted cree una aplicación Server-to-Server OAuth para reemplazar + la funcionalidad de una aplicación JWT en tu cuenta.

    +
    +
      +
    1. logéase en Su perfil Zoom
    2. +
    3. de clic en Avanzado / Marketplace de aplicaciones
    4. +
    5. de clic en Develop / build App
    6. +
    7. escoja JWT o Sever-to-Server OAuth y luego Create
    8. +
    9. Information: ingrese algunas informaciones sobre vuestra \"App\" +(nombres de la aplicación, de la empresa, nombre y dirección de correo de contacto) +
    10. de clic en Continue
    11. +
    12. App Credentials: +
        +
      1. Para una aplicación JWT: copia la clave (API Key) y el código secreto (API Secret) por ingresar aquí.
      2. +
      3. Para una aplicación Server-to-Server OAuth: Copia los valores de Account ID, Client ID y Client secret para ingresar aquí
      4. +
      +
    13. de clic en Continue
    14. +
    15. Feature : activez Event Subscriptions para agregar uno con su endpoint URL +https://your.chamilo.url/plugin/zoom/endpoint.php (valida el endpoint para permitir activar la aplicación) +y agrega este tipo de eventos:

      +
        +
      • Start Meeting
      • +
      • End Meeting
      • +
      • Participant/Host joined meeting
      • +
      • Participant/Host left meeting
      • +
      • Start Webinar
      • +
      • End Webinar
      • +
      • Participant/Host joined webinar
      • +
      • Participant/Host left webinar
      • +
      • All Recordings have completed
      • +
      • Recording transcript files have completed
      • +
      +

      Luego de clic en Done y luego en Save y copie su Verification Token si tiene una aplicación JWT o el Secret token si tiene una aplicación Server-to-Server OAuth para ingresar aquí

    16. +
    17. de clic en Continue
    18. +
    19. Scopes (solo para una aplicación Server-to-Server OAuth): click en Add Scopes y seleccione meeting:write:admin, +webinar:write:admin, recording:write:admin. Luego, haga click en Done.
    20. +
    +
    +Atención : +
    Zoom NO ES un software libre, y reglas específicas de protección de datos se aplican a este. +Por favor verifique con Zoom que éstas le den satisfacción a Usted y los alumnos que la usarán."; + +$strings['enableParticipantRegistration_help'] = "Requiere un perfil Zoom de pago. +No funcionará para un perfil base/gratuito."; + +$strings['enableCloudRecording_help'] = "Requiere un perfil Zoom de pago. +No funcionará para un perfil base/gratuito."; + +// please keep these lines alphabetically sorted +$strings['AllCourseUsersWereRegistered'] = "Todos los alumnos del curso están inscritos"; +$strings['Agenda'] = "Orden del día"; +$strings['CannotRegisterWithoutEmailAddress'] = "No se puede registrar usuario sin dirección de correo electrónico"; +$strings['CopyingJoinURL'] = "Copia de la URL para ingresar"; +$strings['CopyJoinAsURL'] = "Copiar la URL para 'ingresar como'"; +$strings['CopyToCourse'] = "Copiar en el curso"; +$strings['CouldNotCopyJoinURL'] = "Falló la copia de la URL de ingreso"; +$strings['Course'] = "Curso"; +$strings['CreatedAt'] = "Creado el"; +$strings['CreateLinkInCourse'] = "Crear en el curso uno o más vínculos hacia el/los archivo(s)"; +$strings['CreateUserVideoConference'] = "Crear conferencias de usario"; +$strings['DateMeetingTitle'] = "%s: %s"; +$strings['DeleteMeeting'] = "Borrar la conferencia"; +$strings['DeleteFile'] = "Borrar este/estos archivo(s)"; +$strings['Details'] = "Detalle"; +$strings['DoIt'] = "Hágalo"; +$strings['Duration'] = "Duración"; +$strings['DurationFormat'] = "%hh%I"; +$strings['DurationInMinutes'] = "Duración (en minutos)"; +$strings['EndDate'] = "Fecha de fin"; +$strings['EnterMeeting'] = "Ingresar la conferencia"; +$strings['ViewMeeting'] = "Ver la conferencia"; +$strings['Files'] = "Archivos"; +$strings['Finished'] = "terminada"; +$strings['FileWasCopiedToCourse'] = "El archivo ha sido copiado en el curso"; +$strings['FileWasDeleted'] = "El archivo ha sido borrado"; +$strings['GlobalMeeting'] = "Conferencia global"; +$strings['GroupUsersWereRegistered'] = "Miembros de los grupos han sido registrados"; +$strings['InstantMeeting'] = "Conferencia instantánea"; +$strings['Join'] = "Ingresar"; +$strings['JoinGlobalVideoConference'] = "Ingresar la conrencia global"; +$strings['JoinURLCopied'] = "URL para juntarse copiada"; +$strings['JoinURLToSendToParticipants'] = "URL para asistir a la conferencia (para enviar a los participantes)"; +$strings['LiveMeetings'] = "Conferencias activas"; +$strings['LinkToFileWasCreatedInCourse'] = "Un enlace al archivo ha sido añadido al curso"; +$strings['MeetingDeleted'] = "Conferencia borrada"; +$strings['MeetingsFound'] = "Conferencias encontradas"; +$strings['MeetingUpdated'] = "Conferencias actualizadas"; +$strings['NewMeetingCreated'] = "Nueva conferencia creada"; +$strings['Password'] = "Contraseña"; +$strings['RecurringWithFixedTime'] = "Recurrente, a una hora fija"; +$strings['RecurringWithNoFixedTime'] = "Recurrente, sin hora fija"; +$strings['RegisterAllCourseUsers'] = "Inscribir todos los usuarios del curso"; +$strings['RegisteredUserListWasUpdated'] = "Lista de usuarios inscritos actualizada"; +$strings['RegisteredUsers'] = "Usuarios inscritos"; +$strings['RegisteredPresenters'] = "Presentadores registrados"; +$strings['RegisterNoUser'] = "No inscribir ningún usuario"; +$strings['RegisterTheseGroupMembers'] = "Inscribir los miembros de estos grupos"; +$strings['ScheduleAMeeting'] = "Programar una conferencia"; +$strings['ScheduledMeeting'] = "Conferencia programada"; +$strings['ScheduledMeetings'] = "Conferencias programadas"; +$strings['ScheduleAMeeting'] = "Programar una conferencia"; +$strings['SearchMeeting'] = "Buscar una conferencia"; +$strings['Session'] = "Sesión"; +$strings['StartDate'] = "Fecha de inicio"; +$strings['Started'] = "iniciada"; +$strings['StartInstantMeeting'] = "Iniciar una conferencia instantánea"; +$strings['StartMeeting'] = "Iniciar la conferencia"; +$strings['StartTime'] = "Hora de inicio"; +$strings['Topic'] = "Objeto"; +$strings['TopicAndAgenda'] = "Objeto y orden del día"; +$strings['Type'] = "Tipo"; +$strings['UpcomingMeeting'] = "Próximas conferencias"; +$strings['UpdateMeeting'] = "Actualizar la conferencia"; +$strings['UpdateRegisteredUserList'] = "Actualizar la lista de usuarios inscritos"; +$strings['UserRegistration'] = "Inscripción de los usuarios"; +$strings['Y-m-d H:i'] = "d/m/Y a las H\hi"; +$strings['verificationToken'] = 'Verification Token'; +$strings['Waiting'] = "en espera"; +$strings['XRecordingOfMeetingXFromXDurationXDotX'] = "Grabación (%s) de la conferencia %s de %s (%s).%s"; +$strings['YouAreNotRegisteredToThisMeeting'] = "No estás registrado en esta reunión"; +$strings['ZoomVideoConferences'] = "Videoconferencias Zoom"; +$strings['Recordings'] = "Grabaciones"; +$strings['CreateGlobalVideoConference'] = "Crear una videoconferencia global"; +$strings['JoinURLNotAvailable'] = "URL no disponible"; +$strings['Meetings'] = "Conferencias"; +$strings['Activity'] = "Actividad"; +$strings['ConferenceNotAvailable'] = "Conferencia no disponible"; +$strings['SignAttendance'] = "Firmar asistencia"; +$strings['ReasonToSign'] = 'Razón para firmar asistencia'; +$strings['ConferenceWithAttendance'] = "Conferencia con registro de asistencia"; +$strings['Sign'] = "Firmar"; +$strings['Signature'] = "Firma"; +$strings['Meeting'] = "Conferencia"; +$strings['Webinar'] = "Seminario web"; +$strings['AudienceType'] = 'Tipo de público'; +$strings['AccountEmail'] = 'Correo electrónico de la cuenta'; +$strings['NewWebinarCreated'] = "Nuevo seminario web creado"; +$strings['UpdateWebinar'] = 'Actualizar seminario web'; +$strings['WebinarUpdated'] = "Seminario web actualizado"; +$strings['DeleteWebinar'] = "Borrar seminario web"; +$strings['WebinarDeleted'] = "Seminario web borrado"; +$strings['UrlForSelfRegistration'] = "URL para auto registro"; +$strings['RegisterMeToConference'] = "Registrarme a la conferencia"; +$strings['UnregisterMeToConference'] = "Cancelar registro a la conferencia"; +$strings['Presenters'] = "Presentadores"; diff --git a/plugin/zoom/lib/API/Api2RequestTrait.php b/plugin/zoom/lib/API/Api2RequestTrait.php new file mode 100644 index 000000000..76dead10a --- /dev/null +++ b/plugin/zoom/lib/API/Api2RequestTrait.php @@ -0,0 +1,67 @@ + $httpMethod, + CURLOPT_ENCODING => '', + CURLOPT_HTTPHEADER => [ + 'authorization: Bearer '.$this->token, + 'content-type: application/json', + ], + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_MAXREDIRS => 10, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 30, + ]; + if (!is_null($requestBody)) { + $jsonRequestBody = json_encode($requestBody); + if (false === $jsonRequestBody) { + throw new Exception('Could not generate JSON request body'); + } + $options[CURLOPT_POSTFIELDS] = $jsonRequestBody; + } + + $url = "https://api.zoom.us/v2/$relativePath"; + if (!empty($parameters)) { + $url .= '?'.http_build_query($parameters); + } + $curl = curl_init($url); + if (false === $curl) { + throw new Exception("curl_init returned false"); + } + curl_setopt_array($curl, $options); + $responseBody = curl_exec($curl); + $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); + $curlError = curl_error($curl); + curl_close($curl); + + if ($curlError) { + throw new Exception("cURL Error: $curlError"); + } + + if (false === $responseBody || !is_string($responseBody)) { + throw new Exception('cURL Error'); + } + + if (empty($responseCode) + || $responseCode < 200 + || $responseCode >= 300 + ) { + throw new Exception($responseBody, $responseCode); + } + + return $responseBody; + } +} diff --git a/plugin/zoom/lib/API/BaseMeetingTrait.php b/plugin/zoom/lib/API/BaseMeetingTrait.php new file mode 100644 index 000000000..fe9fe5660 --- /dev/null +++ b/plugin/zoom/lib/API/BaseMeetingTrait.php @@ -0,0 +1,41 @@ +token = JWT::encode( + [ + 'iss' => $apiKey, + 'exp' => (time() + 60) * 1000, // will expire in one minute + ], + $apiSecret + ); + self::register($this); + } +} diff --git a/plugin/zoom/lib/API/JsonDeserializableTrait.php b/plugin/zoom/lib/API/JsonDeserializableTrait.php new file mode 100644 index 000000000..f9001104b --- /dev/null +++ b/plugin/zoom/lib/API/JsonDeserializableTrait.php @@ -0,0 +1,122 @@ + $value) { + if (property_exists($destination, $name)) { + if (is_object($value)) { + if (is_object($destination->$name)) { + static::recursivelyCopyObjectProperties($value, $destination->$name); + } else { + throw new Exception("Source property $name is an object, which is not expected"); + } + } elseif (is_array($value)) { + if (is_array($destination->$name)) { + $itemClass = $destination->itemClass($name); + foreach ($value as $sourceItem) { + if ('string' === $itemClass) { + $destination->$name[] = $sourceItem; + } else { + $item = new $itemClass(); + static::recursivelyCopyObjectProperties($sourceItem, $item); + $destination->$name[] = $item; + } + } + } else { + throw new Exception("Source property $name is an array, which is not expected"); + } + } else { + $destination->$name = $value; + } + } else { + error_log("Source object has property $name, which was not expected: ".json_encode($source)); + } + } + $destination->initializeExtraProperties(); + } +} diff --git a/plugin/zoom/lib/API/Meeting.php b/plugin/zoom/lib/API/Meeting.php new file mode 100644 index 000000000..6b9ec94ba --- /dev/null +++ b/plugin/zoom/lib/API/Meeting.php @@ -0,0 +1,88 @@ +tracking_fields = []; + $this->settings = new MeetingSettings(); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('tracking_fields' === $propertyName) { + return TrackingField::class; + } + throw new Exception("no such array property $propertyName"); + } + + /** + * Creates a meeting on the server and returns the resulting MeetingInfoGet. + * + * @throws Exception describing the error (message and code) + * + * @return MeetingInfoGet meeting + */ + public function create($userId = null) + { + $userId = empty($userId) ? 'me' : $userId; + + return MeetingInfoGet::fromJson( + Client::getInstance()->send('POST', "users/$userId/meetings", [], $this) + ); + } + + /** + * Creates a Meeting instance from a topic. + * + * @param string $topic + * @param int $type + * + * @return static + */ + public static function fromTopicAndType($topic, $type = self::TYPE_SCHEDULED) + { + $instance = new static(); + $instance->topic = $topic; + $instance->type = $type; + + return $instance; + } +} diff --git a/plugin/zoom/lib/API/MeetingInfo.php b/plugin/zoom/lib/API/MeetingInfo.php new file mode 100644 index 000000000..832cc33e6 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingInfo.php @@ -0,0 +1,34 @@ +send('GET', "meetings/$id")); + } + + /** + * Updates the meeting on server. + * + * @throws Exception + */ + public function update(): void + { + Client::getInstance()->send('PATCH', 'meetings/'.$this->id, [], $this); + } + + /** + * Ends the meeting on server. + * + * @throws Exception + */ + public function endNow() + { + Client::getInstance()->send('PUT', "meetings/$this->id/status", [], (object) ['action' => 'end']); + } + + /** + * Deletes the meeting on server. + * + * @throws Exception + */ + public function delete() + { + Client::getInstance()->send('DELETE', "meetings/$this->id"); + } + + /** + * Adds a registrant to the meeting. + * + * @param RegistrantSchema $registrant with at least 'email' and 'first_name'. + * 'last_name' will also be recorded by Zoom. + * Other properties remain ignored, or not returned by Zoom + * (at least while using profile "Pro") + * @param string $occurrenceIds separated by comma + * + * @throws Exception + * + * @return CreatedRegistration with unique join_url and registrant_id properties + */ + public function addRegistrant(RegistrantSchema $registrant, string $occurrenceIds = ''): CreatedRegistration + { + return CreatedRegistration::fromJson( + Client::getInstance()->send( + 'POST', + "meetings/$this->id/registrants", + empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds], + $registrant + ) + ); + } + + /** + * Removes registrants from the meeting. + * + * @param MeetingRegistrant[] $registrants registrants to remove (id and email) + * @param string $occurrenceIds separated by comma + * + * @throws Exception + */ + public function removeRegistrants($registrants, $occurrenceIds = '') + { + if (!empty($registrants)) { + Client::getInstance()->send( + 'PUT', + "meetings/$this->id/registrants/status", + empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds], + (object) [ + 'action' => 'cancel', + 'registrants' => $registrants, + ] + ); + } + } + + /** + * Retrieves meeting registrants. + * + * @throws Exception + * + * @return MeetingRegistrantListItem[] the meeting registrants + */ + public function getRegistrants() + { + return MeetingRegistrantList::loadMeetingRegistrants($this->id); + } + + /** + * Retrieves the meeting's instances. + * + * @throws Exception + * + * @return MeetingInstance[] + */ + public function getInstances() + { + return MeetingInstances::fromMeetingId($this->id)->meetings; + } +} diff --git a/plugin/zoom/lib/API/MeetingInstance.php b/plugin/zoom/lib/API/MeetingInstance.php new file mode 100644 index 000000000..efb9539d7 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingInstance.php @@ -0,0 +1,50 @@ +send('GET', 'meetings/'.htmlentities($this->uuid).'/recordings') + ); + } + + /** + * Retrieves the instance's participants. + * + * @throws Exception + * + * @return ParticipantListItem[] + */ + public function getParticipants() + { + return ParticipantList::loadInstanceParticipants($this->uuid); + } +} diff --git a/plugin/zoom/lib/API/MeetingInstances.php b/plugin/zoom/lib/API/MeetingInstances.php new file mode 100644 index 000000000..be38bc902 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingInstances.php @@ -0,0 +1,53 @@ +meetings = []; + } + + /** + * Retrieves a meeting's instances. + * + * @param int $meetingId + * + * @throws Exception + * + * @return MeetingInstances the meeting's instances + */ + public static function fromMeetingId($meetingId) + { + return static::fromJson(Client::getInstance()->send('GET', "past_meetings/$meetingId/instances")); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('meetings' === $propertyName) { + return MeetingInstance::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/MeetingList.php b/plugin/zoom/lib/API/MeetingList.php new file mode 100644 index 000000000..cc21004b5 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingList.php @@ -0,0 +1,58 @@ +meetings = []; + } + + /** + * Retrieves all meetings of a type. + * + * @param int $type TYPE_SCHEDULED, TYPE_LIVE or TYPE_UPCOMING + * + * @throws Exception + * + * @return MeetingListItem[] all meetings + */ + public static function loadMeetings($type) + { + return static::loadItems('meetings', 'users/me/meetings', ['type' => $type]); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('meetings' === $propertyName) { + return MeetingListItem::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/MeetingListItem.php b/plugin/zoom/lib/API/MeetingListItem.php new file mode 100644 index 000000000..e2793bed2 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingListItem.php @@ -0,0 +1,44 @@ +auto_approve = true; + } +} diff --git a/plugin/zoom/lib/API/MeetingRegistrantList.php b/plugin/zoom/lib/API/MeetingRegistrantList.php new file mode 100644 index 000000000..0f8446972 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingRegistrantList.php @@ -0,0 +1,53 @@ +registrants = []; + } + + /** + * Retrieves all registrant for a meeting. + * + * @param int $meetingId + * + * @throws Exception + * + * @return MeetingRegistrantListItem[] all registrants of the meeting + */ + public static function loadMeetingRegistrants($meetingId) + { + return static::loadItems('registrants', "meetings/$meetingId/registrants"); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('registrants' === $propertyName) { + return MeetingRegistrantListItem::class; + } + throw new Exception("no such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/MeetingRegistrantListItem.php b/plugin/zoom/lib/API/MeetingRegistrantListItem.php new file mode 100644 index 000000000..e00b86a26 --- /dev/null +++ b/plugin/zoom/lib/API/MeetingRegistrantListItem.php @@ -0,0 +1,29 @@ +global_dial_in_countries = []; + $this->global_dial_in_numbers = []; + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('global_dial_in_countries' === $propertyName) { + return 'string'; + } + if ('global_dial_in_numbers' === $propertyName) { + return GlobalDialInNumber::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/Ocurrence.php b/plugin/zoom/lib/API/Ocurrence.php new file mode 100644 index 000000000..69843c87f --- /dev/null +++ b/plugin/zoom/lib/API/Ocurrence.php @@ -0,0 +1,22 @@ +send( + 'GET', + $relativePath, + array_merge(['page_size' => $pageSize, 'page_number' => $pageNumber], $parameters) + ) + ); + $items = array_merge($items, $response->$arrayPropertyName); + if (0 === $totalRecords) { + $pageCount = $response->page_count; + $pageSize = $response->page_size; + $totalRecords = $response->total_records; + } + } + if (count($items) !== $totalRecords) { + error_log('Zoom announced '.$totalRecords.' records but returned '.count($items)); + } + + return $items; + } +} diff --git a/plugin/zoom/lib/API/PaginationToken.php b/plugin/zoom/lib/API/PaginationToken.php new file mode 100644 index 000000000..931d370d1 --- /dev/null +++ b/plugin/zoom/lib/API/PaginationToken.php @@ -0,0 +1,71 @@ +send( + 'GET', + $relativePath, + array_merge(['page_size' => $pageSize, 'next_page_token' => $nextPageToken], $parameters) + ) + ); + $items = array_merge($items, $response->$arrayPropertyName); + $nextPageToken = $response->next_page_token; + if (0 === $totalRecords) { + $pageSize = $response->page_size; + $totalRecords = $response->total_records; + } + } while (!empty($nextPagetoken)); + if (count($items) !== $totalRecords) { + error_log('Zoom announced '.$totalRecords.' records but returned '.count($items)); + } + + return $items; + } +} diff --git a/plugin/zoom/lib/API/ParticipantList.php b/plugin/zoom/lib/API/ParticipantList.php new file mode 100644 index 000000000..84c4aba7b --- /dev/null +++ b/plugin/zoom/lib/API/ParticipantList.php @@ -0,0 +1,57 @@ +participants = []; + } + + /** + * Retrieves a meeting instance's participants. + * + * @param string $instanceUUID + * + * @throws Exception + * + * @return ParticipantListItem[] participants + */ + public static function loadInstanceParticipants($instanceUUID) + { + return static::loadItems( + 'participants', + 'past_meetings/'.htmlentities($instanceUUID).'/participants' + ); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('participants' === $propertyName) { + return ParticipantListItem::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/ParticipantListItem.php b/plugin/zoom/lib/API/ParticipantListItem.php new file mode 100644 index 000000000..56e5735b2 --- /dev/null +++ b/plugin/zoom/lib/API/ParticipantListItem.php @@ -0,0 +1,22 @@ +send('GET', 'past_meetings/'.htmlentities($uuid))); + } + + /** + * Retrieves information on participants from a past meeting instance. + * + * @throws Exception + * + * @return ParticipantListItem[] participants + */ + public function getParticipants() + { + return ParticipantList::loadInstanceParticipants($this->uuid); + } +} diff --git a/plugin/zoom/lib/API/QuestionAndAnswer.php b/plugin/zoom/lib/API/QuestionAndAnswer.php new file mode 100644 index 000000000..1eccdcb7c --- /dev/null +++ b/plugin/zoom/lib/API/QuestionAndAnswer.php @@ -0,0 +1,23 @@ + + * `MP4`: Video file of the recording.
    + * `M4A` Audio-only file of the recording.
    + * `TIMELINE`: Timestamp file of the recording. + * To get a timeline file, the "Add a timestamp to the recording" setting must be enabled in the recording settings + * (https://support.zoom.us/hc/en-us/articles/203741855-Cloud-recording#h_3f14c3a4-d16b-4a3c-bbe5-ef7d24500048). + * The time will display in the host's timezone, set on their Zoom profile. + * `TRANSCRIPT`: Transcription file of the recording. + * `CHAT`: A TXT file containing in-meeting chat messages that were sent during the meeting. + * `CC`: File containing closed captions of the recording. + * A recording file object with file type of either `CC` or `TIMELINE` **does not have** the following properties: + * `id`, `status`, `file_size`, `recording_type`, and `play_url`. + */ + public $file_type; + + /** @var int The recording file size. */ + public $file_size; + + /** @var string The URL using which a recording file can be played. */ + public $play_url; + + /** @var string The URL using which the recording file can be downloaded. + * To access a private or password protected cloud recording, you must use a [Zoom JWT App Type] + * (https://marketplace.zoom.us/docs/guides/getting-started/app-types/create-jwt-app). + * Use the generated JWT token as the value of the `access_token` query parameter + * and include this query parameter at the end of the URL as shown in the example. + * Example: `https://api.zoom.us/recording/download/{{ Download Path }}?access_token={{ JWT Token }}` + */ + public $download_url; + + /** @var string The recording status. "completed". */ + public $status; + + /** @var string The time at which recording was deleted. Returned in the response only for trash query. */ + public $deleted_time; + + /** @var string The recording type. The value of this field can be one of the following: + * `shared_screen_with_speaker_view(CC)` + * `shared_screen_with_speaker_view` + * `shared_screen_with_gallery_view` + * `speaker_view` + * `gallery_view` + * `shared_screen` + * `audio_only` + * `audio_transcript` + * `chat_file` + * `TIMELINE` + */ + public $recording_type; + + /** + * Builds the recording file download URL with the access_token query parameter. + * + * @see RecordingFile::$download_url + * + * @param string $token + * + * @return string full URL + */ + public function getFullDownloadURL($token) + { + return $this->download_url.'?access_token='.$token; + } + + /** + * Deletes the file. + * + * @throws Exception + */ + public function delete() + { + Client::getInstance()->send( + 'DELETE', + "/meetings/$this->meeting_id/recordings/$this->id", + ['action' => 'delete'] + ); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/RecordingList.php b/plugin/zoom/lib/API/RecordingList.php new file mode 100644 index 000000000..3cae5dcbb --- /dev/null +++ b/plugin/zoom/lib/API/RecordingList.php @@ -0,0 +1,65 @@ +meetings = []; + } + + /** + * Retrieves all recordings from a period of time. + * + * @param DateTime $startDate first day of the period + * @param DateTime $endDate last day of the period + * + * @throws Exception + * + * @return RecordingMeeting[] all recordings from that period + */ + public static function loadPeriodRecordings($startDate, $endDate) + { + return static::loadItems( + 'meetings', + 'users/me/recordings', + [ + 'from' => $startDate->format('Y-m-d'), + 'to' => $endDate->format('Y-m-d'), + ] + ); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('meetings' === $propertyName) { + return RecordingMeeting::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/RecordingMeeting.php b/plugin/zoom/lib/API/RecordingMeeting.php new file mode 100644 index 000000000..517b532c8 --- /dev/null +++ b/plugin/zoom/lib/API/RecordingMeeting.php @@ -0,0 +1,95 @@ +recording_files = []; + } + + /** + * Deletes the recording on the server. + * + * @throws Exception + */ + public function delete() + { + Client::getInstance()->send( + 'DELETE', + 'meetings/'.htmlentities($this->uuid).'/recordings', + ['action' => 'delete'] + ); + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName) + { + if ('recording_files' === $propertyName) { + return RecordingFile::class; + } + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/RegistrantSchema.php b/plugin/zoom/lib/API/RegistrantSchema.php new file mode 100644 index 000000000..db24b5732 --- /dev/null +++ b/plugin/zoom/lib/API/RegistrantSchema.php @@ -0,0 +1,103 @@ + value */ + public $custom_questions; + + /** + * @var string + */ + public $language; + + /** + * MeetingRegistrant constructor. + */ + public function __construct() + { + $this->status = 'approved'; + $this->custom_questions = []; + } + + public static function fromEmailAndFirstName(string $email, string $firstName, string $lastName = null): RegistrantSchema + { + $instance = new static(); + $instance->first_name = $firstName; + $instance->email = $email; + if (null !== $lastName) { + $instance->last_name = $lastName; + } + + return $instance; + } + + /** + * {@inheritdoc} + */ + public function itemClass($propertyName): string + { + if ('custom_questions' === $propertyName) { + return CustomQuestion::class; + } + throw new Exception("no such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/API/ServerToServerOAuthClient.php b/plugin/zoom/lib/API/ServerToServerOAuthClient.php new file mode 100644 index 000000000..332ce9a7c --- /dev/null +++ b/plugin/zoom/lib/API/ServerToServerOAuthClient.php @@ -0,0 +1,84 @@ +token = $this->requireAccessToken($accountId, $clientId, $clientSecret); + } catch (Exception $e) { + error_log('Zoom: Can\'t require access token: '.$e->getMessage()); + } + + self::register($this); + } + + private function requireAccessToken(string $accountId, string $clientId, string $clientSecret) + { + $options = [ + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_ENCODING => '', + CURLOPT_HTTPHEADER => [ + 'Authorization: Basic '.base64_encode("$clientId:$clientSecret"), + 'Content-Type: application/x-www-form-urlencoded', + 'Host: zoom.us', + ], + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_MAXREDIRS => 10, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 30, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query([ + 'grant_type' => 'account_credentials', + 'account_id' => $accountId, + ]), + ]; + + $url = 'https://zoom.us/oauth/token'; + + $curl = curl_init($url); + + if (false === $curl) { + throw new Exception("curl_init returned false"); + } + + curl_setopt_array($curl, $options); + $responseBody = curl_exec($curl); + $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); + $curlError = curl_error($curl); + curl_close($curl); + + if ($curlError) { + throw new Exception("cURL Error: $curlError"); + } + + if (false === $responseBody || !is_string($responseBody)) { + throw new Exception('cURL Error'); + } + + if (empty($responseCode) || $responseCode < 200 || $responseCode >= 300) { + throw new Exception($responseBody, $responseCode); + } + + $jsonResponseBody = json_decode($responseBody, true); + + if (false === $jsonResponseBody) { + throw new Exception('Could not generate JSON responso body'); + } + + return $jsonResponseBody['access_token']; + } +} diff --git a/plugin/zoom/lib/API/TrackingField.php b/plugin/zoom/lib/API/TrackingField.php new file mode 100644 index 000000000..7f089ff9c --- /dev/null +++ b/plugin/zoom/lib/API/TrackingField.php @@ -0,0 +1,29 @@ + + */ + public $tracking_fields; + public $recurrence; + public $template_id; + /** + * @var array + */ + public $ocurrences; + + protected function __construct() + { + $this->tracking_fields = []; + $this->settings = new WebinarSettings(); + $this->ocurrences = []; + } + + public function itemClass($propertyName): string + { + if ('tracking_fields' === $propertyName) { + return TrackingField::class; + } + + if ('ocurrences' === $propertyName) { + return Ocurrence::class; + } + + throw new Exception("no such array property $propertyName"); + } + + public static function fromTopicAndType($topic, $type = self::TYPE_WEBINAR): WebinarSchema + { + $instance = new static(); + $instance->topic = $topic; + $instance->type = $type; + + return $instance; + } + + /** + * @throws Exception + */ + public function create($userId = null): WebinarSchema + { + $client = Client::getInstance(); + + $userId = empty($userId) ? 'me' : $userId; + + return self::fromJson( + $client->send('POST', "users/$userId/webinars", [], $this) + ); + } + + /** + * @throws Exception + */ + public function update(): void + { + Client::getInstance()->send('PATCH', 'webinars/'.$this->id, [], $this); + } + + /** + * @throws Exception + */ + public function delete() + { + Client::getInstance()->send('DELETE', "webinars/$this->id"); + } + + /** + * @throws Exception + */ + public function addRegistrant(RegistrantSchema $registrant, string $occurrenceIds = ''): CreatedRegistration + { + return CreatedRegistration::fromJson( + Client::getInstance()->send( + 'POST', + "webinars/$this->id/registrants", + empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds], + $registrant + ) + ); + } + + /** + * @throws Exception + */ + public function removeRegistrants(array $registrants, string $occurrenceIds = '') + { + if (empty($registrants)) { + return; + } + + $requestBody = new stdClass(); + $requestBody->action = 'cancel'; + $requestBody->registrants = $registrants; + + Client::getInstance()->send( + 'PUT', + "webinars/$this->id/registrants/status", + empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds], + $requestBody + ); + } +} diff --git a/plugin/zoom/lib/API/WebinarSettings.php b/plugin/zoom/lib/API/WebinarSettings.php new file mode 100644 index 000000000..b46671a78 --- /dev/null +++ b/plugin/zoom/lib/API/WebinarSettings.php @@ -0,0 +1,201 @@ + + */ + public $global_dial_in_countries; + /** + * @var array + */ + public $global_dial_in_numbers; + /** + * @var string + */ + public $contact_name; + /** + * @var string + */ + public $contact_email; + /** + * @var int + */ + public $registrants_restrict_number; + /** + * @var bool + */ + public $registrants_email_notification; + /** + * @var bool + */ + public $post_webinar_survey; + /** + * @var bool + */ + public $meeting_authentication; + /** + * @var QuestionAndAnswer + */ + public $question_and_answer; + /** + * @var bool + */ + public $hd_video_for_attendees; + /** + * @var bool + */ + public $send_1080p_video_to_attendees; + /** + * @var string + */ + public $email_language; + /** + * @var bool + */ + public $panelists_invitation_email_notification; + /** + * @var FollowUpUsers + */ + public $attendees_and_panelists_reminder_email_notification; + /** + * @var FollowUpUsers + */ + public $follow_up_attendees_email_notification; + /** + * @var FollowUpUsers + */ + public $follow_up_absentees_email_notification; + + /** + * @var int + */ + public $registration_type; + /** + * @var string + */ + public $auto; + /** + * @var string + */ + public $survey_url; + /** + * @var string + */ + public $authentication_option; + /** + * @var string + */ + public $authentication_domains; + /** + * @var string + */ + public $authentication_name; + + public function __construct() + { + $this->global_dial_in_countries = []; + $this->global_dial_in_numbers = []; + $this->question_and_answer = new QuestionAndAnswer(); + $this->attendees_and_panelists_reminder_email_notification = new FollowUpUsers(); + $this->follow_up_absentees_email_notification = new FollowUpUsers(); + $this->follow_up_attendees_email_notification = new FollowUpUsers(); + } + + public function itemClass($propertyName): string + { + if ('global_dial_in_countries' === $propertyName) { + return 'string'; + } + + if ('global_dial_in_numbers' === $propertyName) { + return GlobalDialInNumber::class; + } + + throw new Exception("No such array property $propertyName"); + } +} diff --git a/plugin/zoom/lib/MeetingRepository.php b/plugin/zoom/lib/MeetingRepository.php new file mode 100644 index 000000000..392b2ae5b --- /dev/null +++ b/plugin/zoom/lib/MeetingRepository.php @@ -0,0 +1,215 @@ +findAll(); + + /** @var Meeting $candidate */ + foreach ($all as $candidate) { + if (API\Meeting::TYPE_INSTANT === $candidate->getMeetingInfoGet()->type) { + continue; + } + + $cantidateEndDate = clone $candidate->startDateTime; + $cantidateEndDate->add($candidate->durationInterval); + + if (($candidate->startDateTime >= $startDate && $candidate->startDateTime <= $endDate) + || ($candidate->startDateTime <= $startDate && $cantidateEndDate >= $startDate) + ) { + $matching[] = $candidate; + } + } + + return $matching; + } + + /** + * @return ArrayCollection|Collection|Meeting[] + */ + public function globalMeetings() + { + return $this->matching( + Criteria::create()->where( + Criteria::expr()->andX( + Criteria::expr()->eq('course', null), + Criteria::expr()->eq('user', null) + ) + ) + ); + } + + /** + * @return ArrayCollection|Collection|Meeting[] + */ + public function unfinishedGlobalMeetings() + { + return $this->globalMeetings()->filter( + function ($meeting) { + return 'finished' !== $meeting->getMeetingInfoGet()->status; + } + ); + } + + /** + * Returns either a user's meetings or all user meetings. + * + * @param User|null $user + * + * @return QueryBuilder + */ + public function userMeetings($user = null) + { + $qb = $this->createQueryBuilder('m'); + $qb + ->select('m') + ->leftJoin('m.registrants', 'r'); + + //$qb->select('m'); + /*$criteria = Criteria::create()->where( + Criteria::expr()->andX( + Criteria::expr()->isNull('course'), + Criteria::expr()->orX( + Criteria::expr()->isNull('user'), + Criteria::expr()->eq('user', $user) + ) + ));*/ + + /*$qb->where(Criteria::expr()->andX( + Criteria::expr()->isNull('course'), + Criteria::expr()->orX( + Criteria::expr()->isNull('user'), + Criteria::expr()->eq('user', $user) + ) + ));*/ + + $qb + ->andWhere('m.course IS NULL') + ->andWhere('m.user IS NULL OR m.user = :user OR r.user = :user'); + + $qb->setParameters(['user' => $user]); + + return $qb; + + /*return $this->matching( + , + Criteria::expr()->andX( + Criteria::expr()->eq('registrants', null), + Criteria::expr()->orX( + Criteria::expr()->eq('user', null), + Criteria::expr()->eq('user', $user) + ) + ) + ) + );*/ + + /*return $this->matching( + Criteria::create()->where( + Criteria::expr()->andX( + Criteria::expr()->eq('course', null), + Criteria::expr()->orX( + Criteria::expr()->eq('user', null), + Criteria::expr()->eq('user', $user) + ) + ) + ) + );*/ + } + + /** + * @param User|null $user + * + * @return Meeting[] + */ + public function unfinishedUserMeetings($user = null) + { + /*return $this->userMeetings($user)->filter( + function ($meeting) { + return 'finished' !== $meeting->getMeetingInfoGet()->status; + } + );*/ + + $results = @$this->userMeetings($user)->getQuery()->getResult(); + $list = []; + foreach ($results as $meeting) { + if ('finished' === $meeting->getMeetingInfoGet()->status) { + $list[] = $meeting; + } + } + + return $list; + } + + /** + * @param DateTime $start + * @param DateTime $end + * @param User $user + * + * @return ArrayCollection|Collection|Meeting[] + */ + public function periodUserMeetings($start, $end, $user = null) + { + /*return $this->userMeetings($user)->filter( + function ($meeting) use ($start, $end) { + return $meeting->startDateTime >= $start && $meeting->startDateTime <= $end; + } + );*/ + + $results = @$this->userMeetings($user)->getQuery()->getResult(); + $list = []; + if ($results) { + foreach ($results as $meeting) { + if ($meeting->startDateTime >= $start && $meeting->startDateTime <= $end) { + $list[] = $meeting; + } + } + } + + return $list; + } + + /** + * Returns either a course's meetings or all course meetings. + * + * @return ArrayCollection|Collection|Meeting[] + */ + public function courseMeetings(Course $course, CGroupInfo $group = null, Session $session = null) + { + return $this->matching( + Criteria::create()->where( + Criteria::expr()->andX( + Criteria::expr()->eq('group', $group), + Criteria::expr()->eq('course', $course), + Criteria::expr()->eq('session', $session) + ) + ) + ); + } +} diff --git a/plugin/zoom/lib/RecordingRepository.php b/plugin/zoom/lib/RecordingRepository.php new file mode 100644 index 000000000..959a70cbc --- /dev/null +++ b/plugin/zoom/lib/RecordingRepository.php @@ -0,0 +1,65 @@ +findAll(); + foreach ($all as $candidate) { + if ($candidate->startDateTime >= $startDate && $candidate->startDateTime <= $endDate) { + $matching[] = $candidate; + } + } + + return $matching; + } + + /** + * Returns a user's meeting recordings. + * + * @param User $user + * + * @return ArrayCollection|Collection|Recording[] + */ + /*public function userRecordings($user) + { + return $this->matching( + Criteria::create()->where( + Criteria::expr()->in( + 'meeting', + $this->getEntityManager()->getRepository(Meeting::class)->userMeetings($user)->toArray() + ) + ) + ); + }*/ + + /** + * @param DateTime $start + * @param DateTime $end + * @param User $user + * + * @return ArrayCollection|Recording[] + */ + /*public function getPeriodUserRecordings($start, $end, $user = null) + { + return $this->userRecordings($user)->filter( + function ($meeting) use ($start, $end) { + return $meeting->startDateTime >= $start && $meeting->startDateTime <= $end; + } + ); + }*/ +} diff --git a/plugin/zoom/lib/RegistrantRepository.php b/plugin/zoom/lib/RegistrantRepository.php new file mode 100644 index 000000000..0b1b866b8 --- /dev/null +++ b/plugin/zoom/lib/RegistrantRepository.php @@ -0,0 +1,49 @@ +add(new DateInterval('P7D')); + $meetings = $this->getEntityManager()->getRepository(Meeting::class)->periodMeetings($start, $end); + + return $this->findBy(['meeting' => $meetings, 'user' => $user]); + } + + public function findByMeetingPaginated(Meeting $meeting, int $from, int $limit, string $column, string $direction) + { + $queryBuilder = $this->createQueryBuilder('r') + ->join('r.user', 'u') + ->leftJoin('r.signature', 's') + ->where('r.meeting = :meeting') + ->setFirstResult($from) + ->setMaxResults($limit) + ->orderBy($column, $direction) + ; + + $queryBuilder->setParameter('meeting', $meeting); + + return $queryBuilder->getQuery()->getResult(); + } +} diff --git a/plugin/zoom/lib/ZoomPlugin.php b/plugin/zoom/lib/ZoomPlugin.php new file mode 100644 index 000000000..15a5266a2 --- /dev/null +++ b/plugin/zoom/lib/ZoomPlugin.php @@ -0,0 +1,2000 @@ + 'boolean', + 'apiKey' => 'text', + 'apiSecret' => 'text', + 'verificationToken' => 'text', + self::SETTING_ACCOUNT_ID => 'text', + self::SETTING_CLIENT_ID => 'text', + self::SETTING_CLIENT_SECRET => 'text', + self::SETTING_SECRET_TOKEN => 'text', + 'enableParticipantRegistration' => 'boolean', + 'enablePresenter' => 'boolean', + 'enableCloudRecording' => [ + 'type' => 'select', + 'options' => [ + self::RECORDING_TYPE_CLOUD => 'Cloud', + self::RECORDING_TYPE_LOCAL => 'Local', + self::RECORDING_TYPE_NONE => get_lang('None'), + ], + ], + 'enableGlobalConference' => 'boolean', + 'globalConferenceAllowRoles' => [ + 'type' => 'select', + 'options' => [ + PLATFORM_ADMIN => get_lang('Administrator'), + COURSEMANAGER => get_lang('Teacher'), + STUDENT => get_lang('Student'), + STUDENT_BOSS => get_lang('StudentBoss'), + SESSIONADMIN => get_lang('SessionsAdmin'), + ], + 'attributes' => ['multiple' => 'multiple'], + ], + 'accountSelector' => 'text', + ] + ); + + $this->isAdminPlugin = true; + + $accountId = $this->get(self::SETTING_ACCOUNT_ID); + $clientId = $this->get(self::SETTING_CLIENT_ID); + $clientSecret = $this->get(self::SETTING_CLIENT_SECRET); + + if (!empty($accountId) && !empty($clientId) && !empty($clientSecret)) { + $this->jwtClient = new ServerToServerOAuthClient($accountId, $clientId, $clientSecret); + } else { + $this->jwtClient = new JWTClient($this->get('apiKey'), $this->get('apiSecret')); + } + } + + /** + * Caches and returns an instance of this class. + * + * @return ZoomPlugin the instance to use + */ + public static function create(): ZoomPlugin + { + static $instance = null; + + return $instance ? $instance : $instance = new self(); + } + + /** + * @return bool + */ + public static function currentUserCanJoinGlobalMeeting() + { + $user = api_get_user_entity(api_get_user_id()); + + if (null === $user) { + return false; + } + + //return 'true' === api_get_plugin_setting('zoom', 'enableGlobalConference') && api_user_is_login(); + return + 'true' === api_get_plugin_setting('zoom', 'enableGlobalConference') + && in_array( + (api_is_platform_admin() ? PLATFORM_ADMIN : $user->getStatus()), + (array) api_get_plugin_setting('zoom', 'globalConferenceAllowRoles') + ); + } + + /** + * @return array + */ + public function getProfileBlockItems() + { + $elements = $this->meetingsToWhichCurrentUserIsRegisteredComingSoon(); + $addMeetingLink = false; + if (self::currentUserCanJoinGlobalMeeting()) { + $addMeetingLink = true; + } + + if ($addMeetingLink) { + $elements[$this->get_lang('Meetings')] = api_get_path(WEB_PLUGIN_PATH).'zoom/meetings.php'; + } + + $items = []; + foreach ($elements as $title => $link) { + $items[] = [ + 'class' => 'video-conference', + 'icon' => Display::return_icon( + 'bbb.png', + get_lang('VideoConference') + ), + 'link' => $link, + 'title' => $title, + ]; + } + + return $items; + } + + /** + * @return array [ $title => $link ] + */ + public function meetingsToWhichCurrentUserIsRegisteredComingSoon() + { + $linkTemplate = api_get_path(WEB_PLUGIN_PATH).'zoom/join_meeting.php?meetingId=%s'; + $user = api_get_user_entity(api_get_user_id()); + $meetings = self::getRegistrantRepository()->meetingsComingSoonRegistrationsForUser($user); + $items = []; + foreach ($meetings as $registrant) { + $meeting = $registrant->getMeeting(); + + $items[sprintf( + $this->get_lang('DateMeetingTitle'), + $meeting->formattedStartTime, + $meeting->getTopic() + )] = sprintf($linkTemplate, $meeting->getMeetingId()); + } + + return $items; + } + + /** + * @return RegistrantRepository|EntityRepository + */ + public static function getRegistrantRepository() + { + return Database::getManager()->getRepository(Registrant::class); + } + + /** + * Creates this plugin's related tables in the internal database. + * Installs course fields in all courses. + * + * @throws ToolsException + */ + public function install() + { + $schemaManager = Database::getManager()->getConnection()->getSchemaManager(); + + $tablesExists = $schemaManager->tablesExist( + [ + 'plugin_zoom_meeting', + 'plugin_zoom_meeting_activity', + 'plugin_zoom_recording', + 'plugin_zoom_registrant', + 'plugin_zoom_signature', + ] + ); + + if ($tablesExists) { + return; + } + + $em = Database::getManager(); + + (new SchemaTool($em))->createSchema( + [ + $em->getClassMetadata(Meeting::class), + $em->getClassMetadata(Webinar::class), + $em->getClassMetadata(MeetingActivity::class), + $em->getClassMetadata(Recording::class), + $em->getClassMetadata(Registrant::class), + $em->getClassMetadata(Signature::class), + ] + ); + + // Copy icons into the main/img/icons folder + $iconName = 'zoom_meet'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $sourceDir = api_get_path(SYS_PLUGIN_PATH).'zoom/resources/img/'; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $src = $sourceDir.$icon; + $dest = $destinationDir.$icon; + copy($src, $dest); + } + + $this->install_course_fields_in_all_courses(true, 'zoom_meet.png'); + } + + /** + * Drops this plugins' related tables from the internal database. + * Uninstalls course fields in all courses(). + */ + public function uninstall() + { + $em = Database::getManager(); + + (new SchemaTool($em))->dropSchema( + [ + $em->getClassMetadata(Meeting::class), + $em->getClassMetadata(Webinar::class), + $em->getClassMetadata(MeetingActivity::class), + $em->getClassMetadata(Recording::class), + $em->getClassMetadata(Registrant::class), + $em->getClassMetadata(Signature::class), + ] + ); + $this->uninstall_course_fields_in_all_courses(); + + // Remove icons from the main/img/icons folder + $iconName = 'zoom_meet'; + $iconsList = [ + '64/'.$iconName.'.png', + '64/'.$iconName.'_na.png', + '32/'.$iconName.'.png', + '32/'.$iconName.'_na.png', + '22/'.$iconName.'.png', + '22/'.$iconName.'_na.png', + ]; + $destinationDir = api_get_path(SYS_CODE_PATH).'img/icons/'; + foreach ($iconsList as $icon) { + $dest = $destinationDir.$icon; + if (is_file($dest)) { + @unlink($dest); + } + } + } + + /** + * Generates the search form to include in the meeting list administration page. + * The form has DatePickers 'start' and 'end' and Checkbox 'reloadRecordingLists'. + * + * @return FormValidator the form + */ + public function getAdminSearchForm() + { + $form = new FormValidator('search'); + $form->addHeader($this->get_lang('SearchMeeting')); + $form->addDatePicker('start', get_lang('StartDate')); + $form->addDatePicker('end', get_lang('EndDate')); + $form->addButtonSearch(get_lang('Search')); + $oneMonth = new DateInterval('P1M'); + if ($form->validate()) { + try { + $start = new DateTime($form->getSubmitValue('start')); + } catch (Exception $exception) { + $start = new DateTime(); + $start->sub($oneMonth); + } + try { + $end = new DateTime($form->getSubmitValue('end')); + } catch (Exception $exception) { + $end = new DateTime(); + $end->add($oneMonth); + } + } else { + $start = new DateTime(); + $start->sub($oneMonth); + $end = new DateTime(); + $end->add($oneMonth); + } + try { + $form->setDefaults( + [ + 'start' => $start->format('Y-m-d'), + 'end' => $end->format('Y-m-d'), + ] + ); + } catch (Exception $exception) { + error_log(join(':', [__FILE__, __LINE__, $exception])); + } + + return $form; + } + + /** + * @throws Exception + */ + public function getEditConferenceForm(Meeting $conference): FormValidator + { + $isWebinar = $conference instanceof Webinar; + $requiresDateAndDuration = $conference->requiresDateAndDuration(); + + /** @var BaseMeetingTrait $schema */ + $schema = $isWebinar ? $conference->getWebinarSchema() : $conference->getMeetingInfoGet(); + + $form = new FormValidator('edit', 'post', $_SERVER['REQUEST_URI']); + $form->addHeader( + $isWebinar ? $this->get_lang('UpdateWebinar') : $this->get_lang('UpdateMeeting') + ); + $form->addLabel(get_lang('Type'), $conference->typeName); + if ($conference->getAccountEmail()) { + $form->addLabel( + $this->get_lang('AccountEmail'), + $conference->getAccountEmail() + ); + } + $form->addText('topic', $this->get_lang('Topic')); + + if ($requiresDateAndDuration) { + $startTimeDatePicker = $form->addDateTimePicker('startTime', get_lang('StartTime')); + $durationNumeric = $form->addNumeric('duration', $this->get_lang('DurationInMinutes')); + + $form->setRequired($startTimeDatePicker); + $form->setRequired($durationNumeric); + } + + $form->addTextarea('agenda', get_lang('Agenda'), ['maxlength' => 2000]); + $form->addCheckBox('sign_attendance', $this->get_lang('SignAttendance'), get_lang('Yes')); + $form->addTextarea('reason_to_sign', $this->get_lang('ReasonToSign'), ['rows' => 5]); + $form->addButtonUpdate(get_lang('Update')); + + if ($form->validate()) { + $formValues = $form->exportValues(); + + $em = Database::getManager(); + + if ($requiresDateAndDuration) { + $schema->start_time = (new DateTime($formValues['startTime']))->format(DATE_ATOM); + $schema->timezone = date_default_timezone_get(); + $schema->duration = (int) $formValues['duration']; + } + + $schema->topic = $formValues['topic']; + $schema->agenda = $formValues['agenda']; + + $conference + ->setSignAttendance(isset($formValues['sign_attendance'])) + ->setReasonToSignAttendance($formValues['reason_to_sign']); + + try { + $schema->update(); + + if ($isWebinar) { + $conference->setWebinarSchema($schema); + } else { + $conference->setMeetingInfoGet($schema); + } + + $em->persist($conference); + $em->flush(); + + Display::addFlash( + Display::return_message( + $isWebinar ? $this->get_lang('WebinarUpdated') : $this->get_lang('MeetingUpdated'), + 'confirm' + ) + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } + + $defaults = [ + 'topic' => $schema->topic, + 'agenda' => $schema->agenda, + ]; + + if ($requiresDateAndDuration) { + $defaults['startTime'] = $conference->startDateTime->format('Y-m-d H:i'); + $defaults['duration'] = $schema->duration; + } + + $defaults['sign_attendance'] = $conference->isSignAttendance(); + $defaults['reason_to_sign'] = $conference->getReasonToSignAttendance(); + + $form->setDefaults($defaults); + + return $form; + } + + /** + * Generates a meeting delete form and deletes the meeting on validation. + * + * @param Meeting $meeting + * @param string $returnURL where to redirect to on successful deletion + * + * @throws Exception + * + * @return FormValidator + */ + public function getDeleteMeetingForm($meeting, $returnURL) + { + $id = $meeting->getMeetingId(); + $form = new FormValidator('delete', 'post', api_get_self().'?meetingId='.$id); + $form->addButtonDelete($this->get_lang('DeleteMeeting')); + if ($form->validate()) { + $this->deleteMeeting($meeting, $returnURL); + } + + return $form; + } + + public function getDeleteWebinarForm(Webinar $webinar, string $returnURL): FormValidator + { + $id = $webinar->getMeetingId(); + $form = new FormValidator('delete', 'post', api_get_self()."?meetingId=$id"); + $form->addButtonDelete($this->get_lang('DeleteWebinar')); + + if ($form->validate()) { + $this->deleteWebinar($webinar, $returnURL); + } + + return $form; + } + + /** + * @param Meeting $meeting + * @param string $returnURL + */ + public function deleteMeeting($meeting, $returnURL): bool + { + if (null === $meeting) { + return false; + } + + // No need to delete a instant meeting. + if (\Chamilo\PluginBundle\Zoom\API\Meeting::TYPE_INSTANT == $meeting->getMeetingInfoGet()->type) { + return false; + } + + try { + $meeting->getMeetingInfoGet()->delete(); + } catch (Exception $exception) { + $this->handleException($exception); + } + + $em = Database::getManager(); + $em->remove($meeting); + $em->flush(); + + Display::addFlash( + Display::return_message($this->get_lang('MeetingDeleted'), 'confirm') + ); + api_location($returnURL); + + return true; + } + + public function deleteWebinar(Webinar $webinar, string $returnURL) + { + try { + $webinar->getWebinarSchema()->delete(); + } catch (Exception $exception) { + $this->handleException($exception); + } + + $em = Database::getManager(); + $em->remove($webinar); + $em->flush(); + + Display::addFlash( + Display::return_message($this->get_lang('WebinarDeleted'), 'success') + ); + + api_location($returnURL); + } + + /** + * @param Exception $exception + */ + public function handleException($exception) + { + if ($exception instanceof Exception) { + $error = json_decode($exception->getMessage()); + $message = $exception->getMessage(); + if ($error->message) { + $message = $error->message; + } + Display::addFlash( + Display::return_message($message, 'error') + ); + } + } + + /** + * Generates a registrant list update form listing course and session users. + * Updates the list on validation. + * + * @param Meeting $meeting + * + * @throws Exception + * + * @return FormValidator + */ + public function getRegisterParticipantForm($meeting) + { + $form = new FormValidator('register', 'post', $_SERVER['REQUEST_URI']); + $userIdSelect = $form->addSelect('userIds', $this->get_lang('RegisteredUsers')); + $userIdSelect->setMultiple(true); + $form->addButtonSend($this->get_lang('UpdateRegisteredUserList')); + + $selfRegistrationUrl = api_get_path(WEB_PLUGIN_PATH) + .'zoom/subscription.php?meetingId='.$meeting->getMeetingId(); + + $form->addHtml( + '
    +
    + +
    + + + + +
    +
    ' + ); + + $users = $meeting->getRegistrableUsers(); + foreach ($users as $user) { + $userIdSelect->addOption( + api_get_person_name($user->getFirstname(), $user->getLastname()), + $user->getId() + ); + } + + if ($form->validate()) { + $selectedUserIds = $form->getSubmitValue('userIds'); + $selectedUsers = []; + if (!empty($selectedUserIds)) { + foreach ($users as $user) { + if (in_array($user->getId(), $selectedUserIds)) { + $selectedUsers[] = $user; + } + } + } + + try { + $this->updateRegistrantList($meeting, $selectedUsers); + Display::addFlash( + Display::return_message($this->get_lang('RegisteredUserListWasUpdated'), 'confirm') + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } + $registeredUserIds = []; + foreach ($meeting->getRegistrants() as $registrant) { + $registeredUserIds[] = $registrant->getUser()->getId(); + } + $userIdSelect->setSelected($registeredUserIds); + + return $form; + } + + public function getRegisterPresenterForm(Meeting $meeting): FormValidator + { + $form = new FormValidator('register_presenter', 'post', $_SERVER['REQUEST_URI']); + + $presenterIdSelect = $form->addSelect('presenterIds', $this->get_lang('RegisteredPresenters')); + $presenterIdSelect->setMultiple(true); + + $form->addButtonSend($this->get_lang('UpdateRegisteredUserList')); + + $users = $meeting->getRegistrableUsers(); + + foreach ($users as $user) { + $presenterIdSelect->addOption( + api_get_person_name($user->getFirstname(), $user->getLastname()), + $user->getId() + ); + } + + if ($form->validate()) { + $selectedPresenterIds = $form->getSubmitValue('presenterIds') ?: []; + $selectedPresenters = []; + + foreach ($users as $user) { + if (in_array($user->getId(), $selectedPresenterIds)) { + $selectedPresenters[] = $user; + } + } + + try { + $this->updatePresenterList($meeting, $selectedPresenters); + + Display::addFlash( + Display::return_message($this->get_lang('RegisteredUserListWasUpdated'), 'confirm') + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } + + $registeredPresenterIds = []; + + foreach ($meeting->getPresenters() as $registrant) { + if ($registrant instanceof Presenter) { + $registeredPresenterIds[] = $registrant->getUser()->getId(); + } + } + + $presenterIdSelect->setSelected($registeredPresenterIds); + + return $form; + } + + /** + * Generates a meeting recording files management form. + * Takes action on validation. + * + * @param Meeting $meeting + * + * @throws Exception + * + * @return FormValidator + */ + public function getFileForm($meeting, $returnURL) + { + $form = new FormValidator('fileForm', 'post', $_SERVER['REQUEST_URI']); + if (!$meeting->getRecordings()->isEmpty()) { + $fileIdSelect = $form->addSelect('fileIds', get_lang('Files')); + $fileIdSelect->setMultiple(true); + $recordingList = $meeting->getRecordings(); + foreach ($recordingList as &$recording) { + // $recording->instanceDetails = $plugin->getPastMeetingInstanceDetails($instance->uuid); + $options = []; + $recordings = $recording->getRecordingMeeting()->recording_files; + foreach ($recordings as $file) { + $options[] = [ + 'text' => sprintf( + '%s.%s (%s)', + $file->recording_type, + $file->file_type, + $file->file_size + ), + 'value' => $file->id, + ]; + } + $fileIdSelect->addOptGroup( + $options, + sprintf("%s (%s)", $recording->formattedStartTime, $recording->formattedDuration) + ); + } + $actions = []; + if ($meeting->isCourseMeeting()) { + $actions['CreateLinkInCourse'] = $this->get_lang('CreateLinkInCourse'); + $actions['CopyToCourse'] = $this->get_lang('CopyToCourse'); + } + $actions['DeleteFile'] = $this->get_lang('DeleteFile'); + $form->addRadio( + 'action', + get_lang('Action'), + $actions + ); + $form->addButtonUpdate($this->get_lang('DoIt')); + if ($form->validate()) { + $action = $form->getSubmitValue('action'); + $idList = $form->getSubmitValue('fileIds'); + + foreach ($recordingList as $recording) { + $recordings = $recording->getRecordingMeeting()->recording_files; + + foreach ($recordings as $file) { + if (in_array($file->id, $idList)) { + $name = sprintf( + $this->get_lang('XRecordingOfMeetingXFromXDurationXDotX'), + $file->recording_type, + $meeting->getId(), + $recording->formattedStartTime, + $recording->formattedDuration, + $file->file_type + ); + if ('CreateLinkInCourse' === $action && $meeting->isCourseMeeting()) { + try { + $this->createLinkToFileInCourse($meeting, $file, $name); + Display::addFlash( + Display::return_message( + $this->get_lang('LinkToFileWasCreatedInCourse'), + 'success' + ) + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } elseif ('CopyToCourse' === $action && $meeting->isCourseMeeting()) { + try { + $this->copyFileToCourse($meeting, $file, $name); + Display::addFlash( + Display::return_message($this->get_lang('FileWasCopiedToCourse'), 'confirm') + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } elseif ('DeleteFile' === $action) { + try { + $name = $file->recording_type; + $file->delete(); + Display::addFlash( + Display::return_message($this->get_lang('FileWasDeleted').': '.$name, 'confirm') + ); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } + } + } + } + api_location($returnURL); + } + } + + return $form; + } + + /** + * Adds to the meeting course documents a link to a meeting instance recording file. + * + * @param Meeting $meeting + * @param RecordingFile $file + * @param string $name + * + * @throws Exception + */ + public function createLinkToFileInCourse($meeting, $file, $name) + { + $course = $meeting->getCourse(); + if (null === $course) { + throw new Exception('This meeting is not linked to a course'); + } + $courseInfo = api_get_course_info_by_id($course->getId()); + if (empty($courseInfo)) { + throw new Exception('This meeting is not linked to a valid course'); + } + $path = '/zoom_meeting_recording_file_'.$file->id.'.'.$file->file_type; + $docId = DocumentManager::addCloudLink($courseInfo, $path, $file->play_url, $name); + if (!$docId) { + throw new Exception(get_lang(DocumentManager::cloudLinkExists($courseInfo, $path, $file->play_url) ? 'UrlAlreadyExists' : 'ErrorAddCloudLink')); + } + } + + /** + * Copies a recording file to a meeting's course. + * + * @param Meeting $meeting + * @param RecordingFile $file + * @param string $name + * + * @throws Exception + */ + public function copyFileToCourse($meeting, $file, $name) + { + $course = $meeting->getCourse(); + if (null === $course) { + throw new Exception('This meeting is not linked to a course'); + } + $courseInfo = api_get_course_info_by_id($course->getId()); + if (empty($courseInfo)) { + throw new Exception('This meeting is not linked to a valid course'); + } + $tmpFile = tmpfile(); + if (false === $tmpFile) { + throw new Exception('tmpfile() returned false'); + } + $curl = curl_init($file->getFullDownloadURL($this->jwtClient->token)); + if (false === $curl) { + throw new Exception('Could not init curl: '.curl_error($curl)); + } + if (!curl_setopt_array( + $curl, + [ + CURLOPT_FILE => $tmpFile, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 120, + ] + )) { + throw new Exception("Could not set curl options: ".curl_error($curl)); + } + if (false === curl_exec($curl)) { + throw new Exception("curl_exec failed: ".curl_error($curl)); + } + + $sessionId = 0; + $session = $meeting->getSession(); + if (null !== $session) { + $sessionId = $session->getId(); + } + + $groupId = 0; + $group = $meeting->getGroup(); + if (null !== $group) { + $groupId = $group->getIid(); + } + + $newPath = handle_uploaded_document( + $courseInfo, + [ + 'name' => $name, + 'tmp_name' => stream_get_meta_data($tmpFile)['uri'], + 'size' => filesize(stream_get_meta_data($tmpFile)['uri']), + 'from_file' => true, + 'move_file' => true, + 'type' => $file->file_type, + ], + api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document', + '/', + api_get_user_id(), + $groupId, + null, + 0, + 'overwrite', + true, + false, + null, + $sessionId, + true + ); + + fclose($tmpFile); + if (false === $newPath) { + throw new Exception('Could not handle uploaded document'); + } + } + + /** + * Generates a form to fast and easily create and start an instant meeting. + * On validation, create it then redirect to it and exit. + * + * @return FormValidator + */ + public function getCreateInstantMeetingForm( + User $user, + Course $course, + CGroupInfo $group = null, + Session $session = null + ) { + $extraUrl = ''; + if (!empty($course)) { + $extraUrl = api_get_cidreq(); + } + $form = new FormValidator('createInstantMeetingForm', 'post', api_get_self().'?'.$extraUrl, '_blank'); + $form->addButton('startButton', $this->get_lang('StartInstantMeeting'), 'video-camera', 'primary'); + if ($form->validate()) { + try { + $this->startInstantMeeting($this->get_lang('InstantMeeting'), $user, $course, $group, $session); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } + + return $form; + } + + /** + * Generates a form to schedule a meeting. + * On validation, creates it and redirects to its page. + * + * @throws Exception + * + * @return FormValidator + */ + public function getScheduleMeetingForm(User $user, Course $course = null, CGroupInfo $group = null, Session $session = null) + { + $extraUrl = ''; + if (!empty($course)) { + $extraUrl = api_get_cidreq(); + } + $form = new FormValidator('scheduleMeetingForm', 'post', api_get_self().'?'.$extraUrl); + $form->addHeader($this->get_lang('ScheduleAMeeting')); + + $form->addSelect( + 'conference_type', + $this->get_lang('ConferenceType'), + [ + 'meeting' => $this->get_lang('Meeting'), + 'webinar' => $this->get_lang('Webinar'), + ] + ); + $form->addRule('conference_type', get_lang('ThisFieldIsRequired'), 'required'); + + $startTimeDatePicker = $form->addDateTimePicker('startTime', get_lang('StartTime')); + $form->setRequired($startTimeDatePicker); + + $form->addText('topic', $this->get_lang('Topic'), true); + $form->addTextarea('agenda', get_lang('Agenda'), ['maxlength' => 2000]); + + $durationNumeric = $form->addNumeric('duration', $this->get_lang('DurationInMinutes')); + $form->setRequired($durationNumeric); + + if (null === $course && 'true' === $this->get('enableGlobalConference')) { + $options = []; + $options['everyone'] = $this->get_lang('ForEveryone'); + $options['registered_users'] = $this->get_lang('SomeUsers'); + if (!empty($options)) { + if (1 === count($options)) { + $form->addHidden('type', key($options)); + } else { + $form->addSelect('type', $this->get_lang('AudienceType'), $options); + } + } + } else { + // To course + $form->addHidden('type', 'course'); + } + + /* + // $passwordText = $form->addText('password', get_lang('Password'), false, ['maxlength' => '10']); + if (null !== $course) { + $registrationOptions = [ + 'RegisterAllCourseUsers' => $this->get_lang('RegisterAllCourseUsers'), + ]; + $groups = GroupManager::get_groups(); + if (!empty($groups)) { + $registrationOptions['RegisterTheseGroupMembers'] = get_lang('RegisterTheseGroupMembers'); + } + $registrationOptions['RegisterNoUser'] = $this->get_lang('RegisterNoUser'); + $userRegistrationRadio = $form->addRadio( + 'userRegistration', + $this->get_lang('UserRegistration'), + $registrationOptions + ); + $groupOptions = []; + foreach ($groups as $group) { + $groupOptions[$group['id']] = $group['name']; + } + $groupIdsSelect = $form->addSelect( + 'groupIds', + $this->get_lang('RegisterTheseGroupMembers'), + $groupOptions + ); + $groupIdsSelect->setMultiple(true); + if (!empty($groups)) { + $jsCode = sprintf( + "getElementById('%s').parentNode.parentNode.parentNode.style.display = getElementById('%s').checked ? 'block' : 'none'", + $groupIdsSelect->getAttribute('id'), + $userRegistrationRadio->getelements()[1]->getAttribute('id') + ); + + $form->setAttribute('onchange', $jsCode); + } + }*/ + + $form->addCheckBox('sign_attendance', $this->get_lang('SignAttendance'), get_lang('Yes')); + $form->addTextarea('reason_to_sign', $this->get_lang('ReasonToSign'), ['rows' => 5]); + + $accountEmails = $this->getAccountEmails(); + + if (!empty($accountEmails)) { + $form->addSelect('account_email', $this->get_lang('AccountEmail'), $accountEmails); + } + + $form->addButtonCreate(get_lang('Save')); + + if ($form->validate()) { + $formValues = $form->exportValues(); + $conferenceType = $formValues['conference_type']; + $password = substr(uniqid('z', true), 0, 10); + + switch ($formValues['type']) { + case 'everyone': + $user = null; + $group = null; + $course = null; + $session = null; + + break; + case 'registered_users': + //$user = null; + $course = null; + $session = null; + + break; + case 'course': + $user = null; + //$course = null; + //$session = null; + + break; + } + + $accountEmail = $formValues['account_email'] ?? null; + $accountEmail = $accountEmail && in_array($accountEmail, $accountEmails) ? $accountEmail : null; + + try { + $startTime = new DateTime($formValues['startTime']); + + if ('meeting' === $conferenceType) { + $newMeeting = $this->createScheduleMeeting( + $user, + $course, + $group, + $session, + $startTime, + $formValues['duration'], + $formValues['topic'], + $formValues['agenda'], + $password, + isset($formValues['sign_attendance']), + $formValues['reason_to_sign'], + $accountEmail + ); + + Display::addFlash( + Display::return_message($this->get_lang('NewMeetingCreated')) + ); + } elseif ('webinar' === $conferenceType) { + $newMeeting = $this->createScheduleWebinar( + $user, + $course, + $group, + $session, + $startTime, + $formValues['duration'], + $formValues['topic'], + $formValues['agenda'], + $password, + isset($formValues['sign_attendance']), + $formValues['reason_to_sign'], + $accountEmail + ); + + Display::addFlash( + Display::return_message($this->get_lang('NewWebinarCreated')) + ); + } else { + throw new Exception('Invalid conference type'); + } + + if ($newMeeting->isCourseMeeting()) { + if ('RegisterAllCourseUsers' === $form->getSubmitValue('userRegistration')) { + $this->registerAllCourseUsers($newMeeting); + Display::addFlash( + Display::return_message($this->get_lang('AllCourseUsersWereRegistered')) + ); + } elseif ('RegisterTheseGroupMembers' === $form->getSubmitValue('userRegistration')) { + $userIds = []; + foreach ($form->getSubmitValue('groupIds') as $groupId) { + $userIds = array_unique(array_merge($userIds, GroupManager::get_users($groupId))); + } + $users = Database::getManager()->getRepository('ChamiloUserBundle:User')->findBy( + ['id' => $userIds] + ); + $this->registerUsers($newMeeting, $users); + Display::addFlash( + Display::return_message($this->get_lang('GroupUsersWereRegistered')) + ); + } + } + api_location('meeting.php?meetingId='.$newMeeting->getMeetingId().'&'.$extraUrl); + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } + } else { + $form->setDefaults( + [ + 'duration' => 60, + 'userRegistration' => 'RegisterAllCourseUsers', + ] + ); + } + + return $form; + } + + /** + * Return the current global meeting (create it if needed). + * + * @throws Exception + * + * @return string + */ + public function getGlobalMeeting() + { + foreach ($this->getMeetingRepository()->unfinishedGlobalMeetings() as $meeting) { + return $meeting; + } + + return $this->createGlobalMeeting(); + } + + /** + * @return MeetingRepository|EntityRepository + */ + public static function getMeetingRepository() + { + return Database::getManager()->getRepository(Meeting::class); + } + + /** + * Returns the URL to enter (start or join) a meeting or null if not possible to enter the meeting, + * The returned URL depends on the meeting current status (waiting, started or finished) and the current user. + * + * @throws OptimisticLockException + * @throws Exception + * + * @return string|null + */ + public function getStartOrJoinMeetingURL(Meeting $meeting) + { + if ($meeting instanceof Webinar) { + $status = 'started'; + } else { + $status = $meeting->getMeetingInfoGet()->status; + } + + $userId = api_get_user_id(); + $currentUser = api_get_user_entity($userId); + $isGlobal = 'true' === $this->get('enableGlobalConference') && $meeting->isGlobalMeeting(); + + switch ($status) { + case 'ended': + if ($this->userIsConferenceManager($meeting)) { + return $meeting->getMeetingInfoGet()->start_url; + } + break; + case 'waiting': + // Zoom does not allow for a new meeting to be started on first participant join. + // It requires the host to start the meeting first. + // Therefore for global meetings we must make the first participant the host + // that is use start_url rather than join_url. + // the participant will not be registered and will appear as the Zoom user account owner. + // For course and user meetings, only the host can start the meeting. + if ($this->userIsConferenceManager($meeting)) { + return $meeting->getMeetingInfoGet()->start_url; + } + + break; + case 'started': + // User per conference. + if ($currentUser === $meeting->getUser()) { + return $meeting instanceof Webinar + ? $meeting->getWebinarSchema()->start_url + : $meeting->getMeetingInfoGet()->join_url; + } + + // The participant is not registered, he can join only the global meeting (automatic registration). + if ($isGlobal) { + return $this->registerUser($meeting, $currentUser)->getCreatedRegistration()->join_url; + } + + if ($meeting->isCourseMeeting()) { + if ($this->userIsCourseConferenceManager()) { + return $meeting instanceof Webinar + ? $meeting->getWebinarSchema()->start_url + : $meeting->getMeetingInfoGet()->start_url; + } + + $sessionId = api_get_session_id(); + $courseCode = api_get_course_id(); + + if (empty($sessionId)) { + $isSubscribed = CourseManager::is_user_subscribed_in_course( + $userId, + $courseCode, + false + ); + } else { + $isSubscribed = CourseManager::is_user_subscribed_in_course( + $userId, + $courseCode, + true, + $sessionId + ); + } + + if ($isSubscribed) { + if ($meeting->isCourseGroupMeeting()) { + $groupInfo = GroupManager::get_group_properties($meeting->getGroup()->getIid(), true); + $isInGroup = GroupManager::is_user_in_group($userId, $groupInfo); + if (false === $isInGroup) { + throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting')); + } + } + + if (!$meeting instanceof Webinar + && \Chamilo\PluginBundle\Zoom\API\Meeting::TYPE_INSTANT == $meeting->getMeetingInfoGet()->type + ) { + return $meeting->getMeetingInfoGet()->join_url; + } + + return $this->registerUser($meeting, $currentUser)->getCreatedRegistration()->join_url; + } + + throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting')); + } + + //if ('true' === $this->get('enableParticipantRegistration')) { + //if ('true' === $this->get('enableParticipantRegistration') && $meeting->requiresRegistration()) { + // the participant must be registered + $registrant = $meeting->getRegistrantByUser($currentUser); + if (null == $registrant) { + throw new Exception($this->get_lang('YouAreNotRegisteredToThisMeeting')); + } + + // the participant is registered + return $registrant->getCreatedRegistration()->join_url; + //} + break; + } + + return null; + } + + /** + * @param Meeting $meeting + * + * @return bool whether the logged-in user can manage conferences in this context, that is either + * the current course or session coach, the platform admin or the current course admin + */ + public function userIsConferenceManager($meeting) + { + if (null === $meeting) { + return false; + } + + if (api_is_coach() || api_is_platform_admin()) { + return true; + } + + if ($meeting->isCourseMeeting() && api_get_course_id() && api_is_course_admin()) { + return true; + } + + $currentUser = api_get_user_entity(api_get_user_id()); + + if ('true' === $this->get('enableParticipantRegistration') + && 'true' === $this->get('enablePresenter') + && $currentUser + && $meeting->hasUserAsPresenter($currentUser) + ) { + return true; + } + + return $meeting->isUserMeeting() && $meeting->getUser()->getId() == api_get_user_id(); + } + + /** + * @return bool whether the logged-in user can manage conferences in this context, that is either + * the current course or session coach, the platform admin or the current course admin + */ + public function userIsCourseConferenceManager() + { + if (api_is_coach() || api_is_platform_admin()) { + return true; + } + + if (api_get_course_id() && api_is_course_admin()) { + return true; + } + + return false; + } + + /** + * Update local recording list from remote Zoom server's version. + * Kept to implement a future administration button ("import existing data from zoom server"). + * + * @param DateTime $startDate + * @param DateTime $endDate + * + * @throws OptimisticLockException + * @throws Exception + */ + public function reloadPeriodRecordings($startDate, $endDate) + { + $em = Database::getManager(); + $recordingRepo = $this->getRecordingRepository(); + $meetingRepo = $this->getMeetingRepository(); + $recordings = RecordingList::loadPeriodRecordings($startDate, $endDate); + + foreach ($recordings as $recordingMeeting) { + $recordingEntity = $recordingRepo->findOneBy(['uuid' => $recordingMeeting->uuid]); + if (null === $recordingEntity) { + $recordingEntity = new Recording(); + $meeting = $meetingRepo->findOneBy(['meetingId' => $recordingMeeting->id]); + if (null === $meeting) { + try { + $meetingInfoGet = MeetingInfoGet::fromId($recordingMeeting->id); + } catch (Exception $exception) { + $meetingInfoGet = null; // deleted meeting with recordings + } + if (null !== $meetingInfoGet) { + $meeting = $this->createMeetingFromMeeting( + (new Meeting())->setMeetingInfoGet($meetingInfoGet) + ); + $em->persist($meeting); + } + } + if (null !== $meeting) { + $recordingEntity->setMeeting($meeting); + } + } + $recordingEntity->setRecordingMeeting($recordingMeeting); + $em->persist($recordingEntity); + } + $em->flush(); + } + + /** + * @return RecordingRepository|EntityRepository + */ + public static function getRecordingRepository() + { + return Database::getManager()->getRepository(Recording::class); + } + + public function getToolbar(string $returnUrl = ''): string + { + $isPlatformOrSessionAdmin = api_is_platform_admin(true); + $isSessionAdmin = api_is_session_admin(); + + if (!$isPlatformOrSessionAdmin) { + return ''; + } + + $actionsLeft = ''; + $back = ''; + $courseId = api_get_course_id(); + if (empty($courseId)) { + $actionsLeft .= + Display::url( + Display::return_icon('bbb.png', $this->get_lang('Meetings'), null, ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'zoom/meetings.php' + ); + } else { + $actionsLeft .= + Display::url( + Display::return_icon('bbb.png', $this->get_lang('Meetings'), null, ICON_SIZE_MEDIUM), + api_get_path(WEB_PLUGIN_PATH).'zoom/start.php?'.api_get_cidreq() + ); + } + + if (!empty($returnUrl)) { + $back = Display::url( + Display::return_icon('back.png', get_lang('Back'), null, ICON_SIZE_MEDIUM), + $returnUrl + ); + } + + if (!$isSessionAdmin) { + $actionsLeft .= Display::url( + Display::return_icon('agenda.png', get_lang('Calendar'), [], ICON_SIZE_MEDIUM), + 'calendar.php' + ); + + $actionsLeft .= Display::url( + Display::return_icon('settings.png', get_lang('Settings'), null, ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'admin/configure_plugin.php?name=zoom' + ); + } + + return Display::toolbarAction('toolbar', [$back.PHP_EOL.$actionsLeft]); + } + + public function getRecordingSetting() + { + $recording = (string) $this->get('enableCloudRecording'); + + if (in_array($recording, [self::RECORDING_TYPE_LOCAL, self::RECORDING_TYPE_CLOUD], true)) { + return $recording; + } + + return self::RECORDING_TYPE_NONE; + } + + public function hasRecordingAvailable() + { + $recording = $this->getRecordingSetting(); + + return self::RECORDING_TYPE_NONE !== $recording; + } + + /** + * @throws OptimisticLockException + * @throws \Doctrine\ORM\ORMException + */ + public function saveSignature(Registrant $registrant, string $file): bool + { + if (empty($file)) { + return false; + } + + $signature = $registrant->getSignature(); + + if (null !== $signature) { + return false; + } + + $signature = new Signature(); + $signature + ->setFile($file) + ->setRegisteredAt(api_get_utc_datetime(null, false, true)) + ; + + $registrant->setSignature($signature); + + $em = Database::getManager(); + $em->persist($signature); + $em->flush(); + + return true; + } + + public function getSignature(int $userId, Meeting $meeting): ?Signature + { + $signatureRepo = Database::getManager() + ->getRepository(Signature::class) + ; + + return $signatureRepo->findOneBy(['user' => $userId, 'meeting' => $meeting]); + } + + public function exportSignatures(Meeting $meeting, $formatToExport) + { + $signatures = array_map( + function (Registrant $registrant) use ($formatToExport) { + $signature = $registrant->getSignature(); + + $item = [ + $registrant->getUser()->getLastname(), + $registrant->getUser()->getFirstname(), + $signature + ? api_convert_and_format_date($signature->getRegisteredAt(), DATE_TIME_FORMAT_LONG) + : '-', + ]; + + if ('pdf' === $formatToExport) { + $item[] = $signature + ? Display::img($signature->getFile(), '', ['style' => 'width: 150px;'], false) + : '-'; + } + + return $item; + }, + $meeting->getRegistrants()->toArray() + ); + + $data = array_merge( + [ + [ + get_lang('LastName'), + get_lang('FirstName'), + get_lang('DateTime'), + 'pdf' === $formatToExport ? get_lang('File') : null, + ], + ], + $signatures + ); + + if ('pdf' === $formatToExport) { + $params = [ + 'filename' => get_lang('Attendance'), + 'pdf_title' => get_lang('Attendance'), + 'pdf_description' => $meeting->getIntroduction(), + 'show_teacher_as_myself' => false, + ]; + + Export::export_table_pdf($data, $params); + } + + if ('xls' === $formatToExport) { + $introduction = array_map( + function ($line) { + return [ + strip_tags(trim($line)), + ]; + }, + explode(PHP_EOL, $meeting->getIntroduction()) + ); + + Export::arrayToXls( + array_merge($introduction, $data), + get_lang('Attendance') + ); + } + } + + /** + * @throws Exception + */ + public function createWebinarFromSchema(Webinar $webinar, WebinarSchema $schema): Webinar + { + $currentUser = api_get_user_entity(api_get_user_id()); + + $schema->settings->contact_email = $currentUser->getEmail(); + $schema->settings->contact_name = $currentUser->getFullname(); + $schema->settings->auto_recording = $this->getRecordingSetting(); + $schema->settings->registrants_email_notification = false; + $schema->settings->attendees_and_panelists_reminder_email_notification->enable = false; + $schema->settings->follow_up_attendees_email_notification->enable = false; + $schema->settings->follow_up_absentees_email_notification->enable = false; + + $schema = $schema->create($webinar->getAccountEmail()); + + $webinar->setWebinarSchema($schema); + + $em = Database::getManager(); + $em->persist($webinar); + $em->flush(); + + return $webinar; + } + + public function getAccountEmails(): array + { + $currentValue = $this->get('accountSelector'); + + if (empty($currentValue)) { + return []; + } + + $emails = explode(';', $currentValue); + $trimmed = array_map('trim', $emails); + $filtered = array_filter($trimmed); + + return array_combine($filtered, $filtered); + } + + /** + * Register users to a meeting. + * + * @param User[] $users + * + * @throws OptimisticLockException + * + * @return User[] failed registrations [ user id => errorMessage ] + */ + public function registerUsers(Meeting $meeting, array $users) + { + $failedUsers = []; + foreach ($users as $user) { + try { + $this->registerUser($meeting, $user, false); + } catch (Exception $exception) { + $failedUsers[$user->getId()] = $exception->getMessage(); + } + } + Database::getManager()->flush(); + + return $failedUsers; + } + + /** + * @param array $users + * + * @throws OptimisticLockException + * @throws \Doctrine\ORM\ORMException + */ + public function registerPresenters(Meeting $meeting, array $users): array + { + $failedUsers = []; + + foreach ($users as $user) { + try { + $this->registerUser($meeting, $user, false, true); + } catch (Exception $exception) { + $failedUsers[$user->getId()] = $exception->getMessage(); + } + } + + Database::getManager()->flush(); + + return $failedUsers; + } + + /** + * Removes registrants from a meeting. + * + * @param Registrant[] $registrants + * + * @throws Exception + */ + public function unregister(Meeting $meeting, array $registrants) + { + $meetingRegistrants = []; + foreach ($registrants as $registrant) { + $meetingRegistrants[] = $registrant->getMeetingRegistrant(); + } + + if ($meeting instanceof Webinar) { + $meeting->getWebinarSchema()->removeRegistrants($meetingRegistrants); + } else { + $meeting->getMeetingInfoGet()->removeRegistrants($meetingRegistrants); + } + + $em = Database::getManager(); + foreach ($registrants as $registrant) { + $em->remove($registrant); + } + $em->flush(); + } + + /** + * Updates meeting registrants list. Adds the missing registrants and removes the extra. + * + * @param Meeting $meeting + * @param User[] $users list of users to be registered + * + * @throws Exception + */ + private function updateRegistrantList($meeting, $users) + { + $usersToAdd = []; + foreach ($users as $user) { + $found = false; + foreach ($meeting->getRegistrants() as $registrant) { + if ($registrant->getUser() === $user) { + $found = true; + break; + } + } + if (!$found) { + $usersToAdd[] = $user; + } + } + $registrantsToRemove = []; + foreach ($meeting->getRegistrants() as $registrant) { + $found = false; + foreach ($users as $user) { + if ($registrant->getUser() === $user) { + $found = true; + break; + } + } + if (!$found) { + $registrantsToRemove[] = $registrant; + } + } + $this->registerUsers($meeting, $usersToAdd); + $this->unregister($meeting, $registrantsToRemove); + } + + private function updatePresenterList($meeting, $users) + { + /** @var array $presenters */ + $presenters = $meeting->getPresenters(); + + $presenterToAdd = []; + + foreach ($users as $user) { + $foundPresenter = false; + + foreach ($presenters as $presenter) { + if ($presenter->getUser() === $user) { + $foundPresenter = true; + + break; + } + } + + if (!$foundPresenter) { + $presenterToAdd[] = $user; + } + } + + $registrantsToRemove = []; + + foreach ($presenters as $registrant) { + $found = false; + foreach ($users as $user) { + if ($registrant->getUser() === $user) { + $found = true; + break; + } + } + if (!$found) { + $registrantsToRemove[] = $registrant; + } + } + + $this->registerPresenters($meeting, $presenterToAdd); + $this->unregister($meeting, $registrantsToRemove); + } + + /** + * @throws Exception + * @throws OptimisticLockException + * + * @return Registrant + */ + private function registerUser(Meeting $meeting, User $user, $andFlush = true, bool $isPresenter = false) + { + if (empty($user->getEmail())) { + throw new Exception($this->get_lang('CannotRegisterWithoutEmailAddress')); + } + + if ($meeting instanceof Webinar) { + $meetingRegistrant = WebinarRegistrantSchema::fromEmailAndFirstName( + $user->getEmail(), + $user->getFirstname(), + $user->getLastname() + ); + } else { + $meetingRegistrant = MeetingRegistrant::fromEmailAndFirstName( + $user->getEmail(), + $user->getFirstname(), + $user->getLastname() + ); + } + + $registrantEntity = new Registrant(); + + if ($isPresenter) { + $registrantEntity = new Presenter(); + } + + $registrantEntity + ->setMeeting($meeting) + ->setUser($user) + ->setMeetingRegistrant($meetingRegistrant) + ; + + if ($meeting instanceof Webinar) { + $registrantEntity->setCreatedRegistration($meeting->getWebinarSchema()->addRegistrant($meetingRegistrant)); + } else { + $registrantEntity->setCreatedRegistration($meeting->getMeetingInfoGet()->addRegistrant($meetingRegistrant)); + } + + Database::getManager()->persist($registrantEntity); + + if ($andFlush) { + Database::getManager()->flush($registrantEntity); + } + + return $registrantEntity; + } + + /** + * Starts a new instant meeting and redirects to its start url. + * + * @param string $topic + * @param User|null $user + * @param Course|null $course + * @param CGroupInfo|null $group + * @param Session|null $session + * + * @throws Exception + */ + private function startInstantMeeting($topic, $user = null, $course = null, $group = null, $session = null) + { + $meetingInfoGet = MeetingInfoGet::fromTopicAndType($topic, MeetingInfoGet::TYPE_INSTANT); + //$meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE; + $meeting = $this->createMeetingFromMeeting( + (new Meeting()) + ->setMeetingInfoGet($meetingInfoGet) + ->setUser($user) + ->setGroup($group) + ->setCourse($course) + ->setSession($session) + ); + api_location($meeting->getMeetingInfoGet()->start_url); + } + + /** + * Creates a meeting on Zoom servers and stores it in the local database. + * + * @param Meeting $meeting a new, unsaved meeting with at least a type and a topic + * + * @throws Exception + * + * @return Meeting + */ + private function createMeetingFromMeeting($meeting) + { + $currentUser = api_get_user_entity(api_get_user_id()); + + $meeting->getMeetingInfoGet()->settings->contact_email = $currentUser->getEmail(); + $meeting->getMeetingInfoGet()->settings->contact_name = $currentUser->getFullname(); + $meeting->getMeetingInfoGet()->settings->auto_recording = $this->getRecordingSetting(); + $meeting->getMeetingInfoGet()->settings->registrants_email_notification = false; + + //$meeting->getMeetingInfoGet()->host_email = $currentUser->getEmail(); + //$meeting->getMeetingInfoGet()->settings->alternative_hosts = $currentUser->getEmail(); + + // Send create to Zoom. + $meeting->setMeetingInfoGet( + $meeting->getMeetingInfoGet()->create( + $meeting->getAccountEmail() + ) + ); + + Database::getManager()->persist($meeting); + Database::getManager()->flush(); + + return $meeting; + } + + /** + * @throws Exception + * + * @return Meeting + */ + private function createGlobalMeeting() + { + $meetingInfoGet = MeetingInfoGet::fromTopicAndType( + $this->get_lang('GlobalMeeting'), + MeetingInfoGet::TYPE_SCHEDULED + ); + $meetingInfoGet->start_time = (new DateTime())->format(DATE_ATOM); + $meetingInfoGet->duration = 60; + $meetingInfoGet->settings->approval_type = + ('true' === $this->get('enableParticipantRegistration')) + ? MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE + : MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED; + // $meetingInfoGet->settings->host_video = true; + $meetingInfoGet->settings->participant_video = true; + $meetingInfoGet->settings->join_before_host = true; + $meetingInfoGet->settings->registrants_email_notification = false; + + return $this->createMeetingFromMeeting((new Meeting())->setMeetingInfoGet($meetingInfoGet)); + } + + /** + * Schedules a meeting and returns it. + * set $course, $session and $user to null in order to create a global meeting. + * + * @param DateTime $startTime meeting local start date-time (configure local timezone on your Zoom account) + * @param int $duration in minutes + * @param string $topic short title of the meeting, required + * @param string $agenda ordre du jour + * @param string $password meeting password + * + * @throws Exception + * + * @return Meeting meeting + */ + private function createScheduleMeeting( + User $user = null, + Course $course = null, + CGroupInfo $group = null, + Session $session = null, + $startTime, + $duration, + $topic, + $agenda, + $password, + bool $signAttendance = false, + string $reasonToSignAttendance = '', + string $accountEmail = null + ) { + $meetingInfoGet = MeetingInfoGet::fromTopicAndType($topic, MeetingInfoGet::TYPE_SCHEDULED); + $meetingInfoGet->duration = $duration; + $meetingInfoGet->start_time = $startTime->format(DATE_ATOM); + $meetingInfoGet->agenda = $agenda; + $meetingInfoGet->password = $password; + $meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED; + if ('true' === $this->get('enableParticipantRegistration')) { + $meetingInfoGet->settings->approval_type = MeetingSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE; + } + + return $this->createMeetingFromMeeting( + (new Meeting()) + ->setMeetingInfoGet($meetingInfoGet) + ->setUser($user) + ->setCourse($course) + ->setGroup($group) + ->setSession($session) + ->setSignAttendance($signAttendance) + ->setReasonToSignAttendance($reasonToSignAttendance) + ->setAccountEmail($accountEmail) + ); + } + + /** + * @throws Exception + */ + private function createScheduleWebinar( + ?User $user, + ?Course $course, + ?CGroupInfo $group, + ?Session $session, + DateTime $startTime, + $duration, + $topic, + $agenda, + $password, + bool $signAttendance = false, + string $reasonToSignAttendance = '', + string $accountEmail = null + ): Webinar { + $webinarSchema = WebinarSchema::fromTopicAndType($topic); + $webinarSchema->duration = $duration; + $webinarSchema->start_time = $startTime->format(DATE_ATOM); + $webinarSchema->agenda = $agenda; + $webinarSchema->password = $password; + + if ('true' === $this->get('enableParticipantRegistration')) { + $webinarSchema->settings->approval_type = WebinarSettings::APPROVAL_TYPE_AUTOMATICALLY_APPROVE; + } + + $webinar = (new Webinar()) + ->setUser($user) + ->setCourse($course) + ->setGroup($group) + ->setSession($session) + ->setSignAttendance($signAttendance) + ->setReasonToSignAttendance($reasonToSignAttendance) + ->setAccountEmail($accountEmail) + ; + + return $this->createWebinarFromSchema($webinar, $webinarSchema); + } + + /** + * Registers all the course users to a course meeting. + * + * @param Meeting $meeting + * + * @throws OptimisticLockException + */ + private function registerAllCourseUsers($meeting) + { + $this->registerUsers($meeting, $meeting->getRegistrableUsers()); + } +} diff --git a/plugin/zoom/meeting.ajax.php b/plugin/zoom/meeting.ajax.php new file mode 100644 index 000000000..a3aa888a5 --- /dev/null +++ b/plugin/zoom/meeting.ajax.php @@ -0,0 +1,50 @@ +get('meetingId', 0); + +if (empty($meetingId)) { + api_not_allowed(); +} + +$plugin = ZoomPlugin::create(); +/** @var Meeting $meeting */ +$meeting = $plugin->getMeetingRepository()->findOneBy(['meetingId' => $meetingId]); +$currentUserId = api_get_user_id(); +$currentUser = api_get_user_entity($currentUserId); + +if (null === $meeting) { + api_not_allowed(false, $plugin->get_lang('MeetingNotFound')); +} + +switch ($httpRequest->get('a')) { + case 'sign_attempt': + $registrant = $meeting->getRegistrantByUser($currentUser); + + if (!$meeting->isSignAttendance() || + null === $registrant + ) { + api_not_allowed(); + } + + $file = $httpRequest->request->get('file', ''); + + $secToken = Security::get_token('zoom_signature'); + + if (!Security::check_token($secToken, null, 'zoom_signature')) { + api_not_allowed(); + } + + echo (int) $plugin->saveSignature($registrant, $file); + exit; +} diff --git a/plugin/zoom/meeting.php b/plugin/zoom/meeting.php new file mode 100644 index 000000000..185932b72 --- /dev/null +++ b/plugin/zoom/meeting.php @@ -0,0 +1,110 @@ +getMeetingRepository()->findOneBy(['meetingId' => $meetingId]); + +if (null === $meeting) { + api_not_allowed(true, $plugin->get_lang('MeetingNotFound')); +} + +$course_plugin = 'zoom'; // needed in order to load the plugin lang variables +$returnURL = 'meetings.php'; +$urlExtra = ''; +if ($meeting->isCourseMeeting()) { + api_protect_course_script(true); + $this_section = SECTION_COURSES; + $urlExtra = api_get_cidreq(); + $returnURL = 'start.php?'.$urlExtra; + + if (api_is_in_group()) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.$urlExtra, + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.$urlExtra, + 'name' => get_lang('GroupSpace').' '.$meeting->getGroup()->getName(), + ]; + } +} + +$logInfo = [ + 'tool' => 'Videoconference Zoom', +]; +Event::registerLog($logInfo); + +$interbreadcrumb[] = [ + 'url' => $returnURL, + 'name' => $plugin->get_lang('ZoomVideoConferences'), +]; + +$tpl = new Template($meeting->getMeetingId()); + +if ($plugin->userIsConferenceManager($meeting)) { + // user can edit, start and delete meeting + $tpl->assign('isConferenceManager', true); + + $tpl->assign('editMeetingForm', $plugin->getEditConferenceForm($meeting)->returnForm()); + + if ($meeting instanceof Webinar) { + $tpl->assign('deleteMeetingForm', $plugin->getDeleteWebinarForm($meeting, $returnURL)->returnForm()); + } elseif ($meeting instanceof Meeting) { + $tpl->assign('deleteMeetingForm', $plugin->getDeleteMeetingForm($meeting, $returnURL)->returnForm()); + } + + $pluginEnableParticipantRegistration = 'true' === $plugin->get('enableParticipantRegistration'); + + if ($pluginEnableParticipantRegistration && $meeting->requiresRegistration()) { + if (false === $meeting->isGlobalMeeting() + && false == $meeting->isCourseMeeting() + ) { + $tpl->assign('registerParticipantForm', $plugin->getRegisterParticipantForm($meeting)->returnForm()); + $tpl->assign('registrants', $meeting->getRegistrants()); + } + + if ('true' === $plugin->get('enablePresenter') && !$meeting->isCourseMeeting()) { + $tpl->assign('registerPresenterForm', $plugin->getRegisterPresenterForm($meeting)->returnForm()); + $tpl->assign('presenters', $meeting->getPresenters()); + } + } + + if (ZoomPlugin::RECORDING_TYPE_NONE !== $plugin->getRecordingSetting() && + $meeting->hasCloudAutoRecordingEnabled() + ) { + $tpl->assign('fileForm', $plugin->getFileForm($meeting, $returnURL)->returnForm()); + $tpl->assign('recordings', $meeting->getRecordings()); + } +} elseif ($meeting->requiresRegistration()) { + $userId = api_get_user_id(); + try { + foreach ($meeting->getRegistrants() as $registrant) { + if ($registrant->getUser()->getId() == $userId) { + $tpl->assign('currentUserJoinURL', $registrant->getJoinUrl()); + break; + } + } + } catch (Exception $exception) { + Display::addFlash( + Display::return_message($exception->getMessage(), 'error') + ); + } +} + +$tpl->assign('actions', $plugin->getToolbar()); +$tpl->assign('meeting', $meeting); +$tpl->assign('url_extra', $urlExtra); +$tpl->assign('content', $tpl->fetch('zoom/view/meeting.tpl')); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/meetings.php b/plugin/zoom/meetings.php new file mode 100644 index 000000000..fb2b6e760 --- /dev/null +++ b/plugin/zoom/meetings.php @@ -0,0 +1,28 @@ +getAdminSearchForm(); +$startDate = new DateTime($form->getElement('start')->getValue()); +$endDate = new DateTime($form->getElement('end')->getValue()); +$scheduleForm = $plugin->getScheduleMeetingForm($user); +$tpl = new Template(); +$tpl->assign('meetings', $plugin->getMeetingRepository()->periodUserMeetings($startDate, $endDate, $user)); +$tpl->assign('allow_recording', $plugin->hasRecordingAvailable()); +$tpl->assign('actions', $plugin->getToolbar()); +$tpl->assign('search_form', $form->returnForm()); +$tpl->assign('schedule_form', $scheduleForm->returnForm()); +$tpl->assign('content', $tpl->fetch('zoom/view/meetings.tpl')); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/plugin.php b/plugin/zoom/plugin.php new file mode 100644 index 000000000..8db7cffa8 --- /dev/null +++ b/plugin/zoom/plugin.php @@ -0,0 +1,4 @@ +get_info(); diff --git a/plugin/zoom/resources/img/22/zoom_meet.png b/plugin/zoom/resources/img/22/zoom_meet.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3ba9a977a6985c64ae35d0e4f349c6123b5d3f GIT binary patch literal 4640 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 WnK|}2Btnt^0000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3&sa^pG@g#Y6da|G-K90zN}yTKfPz5*#(cB|dX z8!If(Ac&=w%q-fh|NgVhfA|Smiit@|t~pzNVvW@|E{eT=>iKpyp6~0AeBX1Q`_1!) zrz_Bg>rZL-_(p&Fc;K;y>!15gx$pRLPjugSOc-=!&&7P*B>n#Cck!O6uInP#BClKR z{%XfP@NDJ&UEx|L+>7bo=CvaVR+@>0g7+)9V9$5Cb`Y-LMvvp0hZyHh4?pHK_1*~v z`En6YTSBg&eGb@Ukqc1#}5W0{Zeo4A(qseFoUcHr`9hwXfZtIlKbZCrHI zHMiY5?QnxFM!&q!dOv(PeI1Nw*0bJldd%q< z>K&6g9{K6Ve0TFtK4$NsCUUksZNhe&*?X#aERNt-_kMg<8!;;zQhlqiU-p% zbK!u^_G^ls+(&GMGcxZ9FcE7fHbV+TXR`~*WoN~EaSl5c9Mssk57Bo# z;8OCFP5Kx@_C|d1x;D>MB2`)mC6-ijky1)6t@;|kz*KXUT57GWdDB>^F;`=Jqo?~G zdhDs^F1_^H+aP>K9BJesql`M*^fOFgV&*Bc%sSh0W`PtduC(%!RaRYX6Kgx{xYN#C zcG-2er`K+;{_yz&uesZ6{&-5q$&Zq63zWeapZ~4un@>_oMzw(?>*Zn^{XK>w*etYA!sXRB1 z$F3|?o7zD3^&OLoCMh*>@xJw~%popr9`Jqc9htAJ(qvazJIeWHg=W&-w=taER+R8o zItQ&yat2}5#J#qZ;@oxN3?{>vEzh{JTXv7RnJCIiyH8~<7FuIVLlk`O$Q5+c=VPPK z>*QZs^RpYDTQ};Kk(51~%;l0lN7vi#idW-O=!GM-8F5s^maWyTVKp2LPcYKy0h+_- zI40C}&%R{B2h=LD+>xHIv>x+4>rNp>&#n39%m^|du$J7{$) zO@)moR|q@SW6srbDSNHGLfmTZeRV<L z9ER-oFXM^*py8wQk`GuH7$w?WUuy=ni~bo=CBDvB=G3nGHXsR8wpDfSc6AORc_fZ{ z4(la0AyK8BwRG{b^)u0Z@mFn`TXL<*OEN9a7VaJDR_oTtU{_5oQ1o1Tblp=)_{@j7 z82e!%@RWc}!H}?;ESt${KmcaF@1m#$?K%0`|{^Z|J>57MQH~Apj%>jSQDX&W0ggsi(N@OYb=9XpvDpghxfY9W$x~S8X|;ir)w#f?&K;Q)j(LLt;l-l$}dE|=KDHKW8P8FVqRFaVyd){XAm%B zPl>K&TvLKt9b#4dn<&%0i=lgiDJydB++E}T!uH_|bY$0C&iLF~cngG_Y9`lUbQ|zJ zR$->ZcdvMd**&qtuF~=AS`~lJe6C-{nM^82T!WQ!8x0vfw1^VK2@^ZtMIy?eYly=FxxISH$&eXjELt%9%MB<0-DR_GI! zK$~#zKD+t>vl>SwG04GKE2?65>_aWpP^X=kpl2c71DS6D@aXYO`{bENYkoy(5|NXl zY0(~Ou_Sj?705GZ%MoJAVHq~OcP?t8as?S)pq{&(uu_fy3flo1up0!Ffw{!a^Wm7> zFQuy*68;1B))W|8=9z3lSj42aD(iMXRD}R&$(`pPPd^`~I(m8j$ERPO{a8D6iW8EX zM+$P75ddB_uj`j_!K}IwH?0oBGHLAHEsSghbR`fdl1Mvr*2S86PN#g%RC6vOZ4v}W?fnXl zPHSE}dh7V>M9#%rT0kNgITnR96@j9oZ|)G10|84^tN{%6MvP*z^+ZVlD=3W4(9^Az z>~-V&`oNrvMdd}tT;iR;q3|OfC;TB5+ahzs4!%yxSvHPSsU&v?B9?9>P-SECKn|UJ zci|%8UQP?uKuno@LLd^@lq1AsQ;c@Z1j(Wp^}u}cPP1kwSpC!ehePc?ME8Px{pz^1{Ty12A^>4M0@V;!_KWs@ zWGf^#;RIk7)W?rKQG^D144lEkbQ)Nu?L193M_&u_2C803xv{mGWv$TLE zW)I&v%-xgeZ*QrJj!D^%ay)40d3OsQ(oN_8ddGa+dM*tE#^bS1^Ir?Uzr(ub?ZQ7> z_GQfzy*%2wa>dzE{XBcVuxBZ|Md$iu9SR#Q9jTIuWJWI#_KG5Lr!~z5ecMt|`Wt;; zEWt=Mk6=?l*_X$29kDlK4pQ>+4(S=AVgkkPpo)Mc{piMfS1M@)%;l=W$$1rvF$u63 z2GunQaCQZPH$@x~56ElL=|>#^`0B)!St%&7L33b5Ay_FQ8nH|{qndFXR#+-3i9`nj zbQjv7Q0b2KwUnSx>2(;b_j}C&wIUQvSg|Bp>03 z7VcuV+k{uO%BQ1J`qqUQ0x3Q5;f|WuMHEzCrZc}>?>F0fB84OjN<<=BJg9h!zoN&Z z_@*K`TYP;J1PN{}Me#3QF{u9r{EX|h4)sgYcRmcCGvgk>nR%e}pxVEFeHRs;kzxM! zJ;V|6RgoQ!P8-c87(Em{@EHzP37hmq5nP*|J4}%EAf)xx&rg_FnYKHUWGE0{dQu-l zQ<9uWA)?k@L5o&~=LCtw{5s{P_KF()1*8Tcv;Y1z+&67H4@OA57~S_;*QWinKp0j1 zomA=qhFwa2B0}E^vocw-TPOj@t5P z^WU`yXvl8O{{R<0UhqEIaqj>C0fcEoLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~m! zuSy3yh=^vWP8LK(9JLBXs1Ry}Rvk<({emV9NsEi4;979-W3lSs;;gHKs~`w|fH*li zDY{6B|4RxjVmvtR$GdxvyLW(4D>BV$8wWJKY$jq+F_m5wJ6_R)A#`CFL77>`oFv8J zIlk`UVo-o6w3YuHrYq!67i3qwIB$cXzh-_V1ZSe?MAna-B7i zM}hzV0E$pdR7C(m8;_5VkdTm(k&%*;l9Q8@l$4Z}m6ev3mY0{8n3$NEnVFiJnwy)O ztE;Q7uCB4Mv9hwVv$M0bw6wOiwzs#pxw*Nzy1Ko+y}-c0!NI}A#Kg+V%FD~k%*@Qq z&CSrz(ACw|*VotC+1cCM+uq*Z;^N}v<>lt)=I7_<>FMe1?d|mR^!E1lWyle@00001 zbW%=J06^y0W&i*H0b)x>L;#2d9Y_EG010qNS#tmY2cG}{2cH2jWDM*8000McNliru zvLv`)xn){M z!L)PRxZ9hRWJ8i0&sp`bJ0aC)ki7o$;rHBP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1bxk|QY${O1&V1P~I4kMwF;k%)jVHD%EXuLO5fxl^nhwlyq`NL*pIi2MJt$5d0iqPJS->mbUhduE zf4SUR-5?5H?^r=EGS7k<#+>~^3WS7p@g^JK+jY_JAN&Wd3I=7vtT3?pcD85|K71=~ zo+IQ0;|ZbQ^0+ntL^xZZ3_^f|Pa#T3!Dpfz0&r9iRG~RVrUnLr2n{Gn9AhGvNy*N} zJ2`Aw5=vy#$W0(ZVu~5ulvqI)v%r5cD}1O*6c&*rS=339nquN4DW#k&$<-V)XUQq& zthwZ>QCUTkW>qa(3zdi*C?%C#w3JdSxr&1-_NtJtm}%5v<0dV&+_aTeJKU#pm!5j= z+Dos4MjS{JBaJ+Clu>7J6PaT2l$mCpI?JpJzP94xB`dAGbd^=_tX-^rx&8xd;bM(8 zQzn=1tYOyWeA^;eIuYXxj1dPgo{a%GG#O`J12Y+O#+fgKpb&zRV%)e9j4@EiGmxxs z=k9~KAMxfyf5aR8iaBT0{R!qAsQb#>3)Y6!xxN`YQD~U@i0EsPC9sn`kd0yr_9RxP z{@>_+cY~j7#Em63FI^3~sWm6xN|0E~Vhd8dvOG=9ZBkFh}@zVBgmdxQGFcU z^Ms*MD(a1H<%H?QTQ5fUP9RB~rlHwnu~JJo+p~VL!r6^^HE5bPRl~1=eKopw0!X@p z#OmF~hJF76vqjZJfK|tIK^Tus885;U>JqBdIJe z+ZcCG6Nv|UDj8F3Se#2jPFp2IZC%;{!-~F6a|AeRD>)SHZJ`@!K&Ju+6s?t-nHi;* zk&()6$kVqukY6Xm7x?JhTA%5&=bLBi&GCQ7NcU~ZgONyK!y|~Hv{+KtOpBzqd^Yr9 z@Lhj*3>aunj|DIgdR;LvS1k7|blrTTzk4tMAp@>q0+M8cG|DBg$moHnSaP*Fr~tiQ zJi7PGkB@2z896RdOND1o$iwDK`%HCE9(AmA8ks;l%nSvzH-!(&Lgy=wWT}yLXt?;O z{sg_VEU`9tq8~{aR_^S;J?Tnxwwc7M@;7&;vQycKAVqn)}}i3a-$r2-?xoD!utdqLgeN* zW*{4$){EYwbmvH;dUgPeJ|VTnb-`|Q89=m|bDP7unc!BoRYJXaSC>W5s7MYmB^kLi z$vCzz_+Z5UpW^Jl2@G#bB6=2-u!0^&a)k}0BWtIjrN@l^CgBPN|9r;mb5%i3OU0`8 z&PZ;M^*$xdAc**OGC#VWWPkl`rSU2E?8+dhI6xzE_tqDENq>9>9^%`%2F0004mX+uL$Nkc;* zaB^>EX>4Tx0C=2zkv&MmKpe$iQ^l`J2Rn#}W~fdUL`5963Pq?8YK2xEOfLO`CJjl8 zi=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT3N2zhIPS;0dyl(!fKV$k&1xG5G`(yl zVo@=bUKKlD(Ssp$VHiP~S;m|s#o;-=?&0I>U4&bnM}+Rhls^o3o9+m@}@>SMI2T&o$`g0 z%PQwB&RVg;8u#Qc3}p0`Wvu?Dkz|cG9t9N1;P@qSDdZ}E zkz)ZRXpkL0_#gc4*2+#!xJiL1(EeiEA0t4=E>N%9_V=-E*G~ZdGjOFh{iPZ(^GSNO zsfCY#;5Kk^-PDvl;Bp7(e==lKb|gPdA)5u>&*+=dK;JFUz2^4T*vIJukfg5SH^9Lm zFq)(6b&q#vqlDYGhy7HE~@tM5wn!WL$!0@HR@211bKAI z!_w%=*5}CC^3T}j%iHtJ+w;oZ_|Dz)(%|;u;^N!q_}}OB-{|z>>h|O6_UZ2R^Yr%h z_V)Jn_Wa-HBLDyZ0d!JMQvg8b*k%9#00Cl4M??UK1szBL000SaNLh0L00*A{00*A| zFk}qu00007bV*G`2jmML2_XZVP@P`@007EKL_t(I%VS`mGT;&t!XqoKtO*7(8d#-e zG?9QberX^;H$)Q?pg2Yqivmp%B-d$T1$Zq00R@=6h9&_OAhvEO0{EH&1&o1M7Rdr+ z1)*M=nl6qAS7=~S;1cSHa3>ZMTte|H@bSl@ARGz!g@lKrE6|Ta0%6f{ac=k&1bGus z;3w?|QJ{?o2%Q)t1p&&ker`yy0XD)Fp(5H67>K4w3UokhWeIf{5Yj-8Ph62jD830K sIZZ-|l7T?7#gZ`yNP~bDerYlR02F|Jhw9caiU0rr07*qoM6N<$f{woz2><{9 literal 0 HcmV?d00001 diff --git a/plugin/zoom/resources/img/32/zoom_meet_na.png b/plugin/zoom/resources/img/32/zoom_meet_na.png new file mode 100644 index 0000000000000000000000000000000000000000..28b3a7ce0941e4be5aacc4a9814b014a43c5ade3 GIT binary patch literal 2065 zcmV+s2=4cZP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O=0{vhyem{Ld-o2uL87FApfm)A0`m+M{JxjNgK+#58p0hfVIFNw2<)~?XnKzuohG)QUD+_F=Li;*X1NaoIA z>q^L6xd-DZTBF4|)G$H;mz}2rXEE^I7ut5~w%4h^@*qrQ!eWG1K5Xu@!Jjm@RyT;q zmpfMQi!H9&Mki~$nX-BL&Kk5X z=i3rw8rIEiJ^ox3;YzQvo9 z@-5!@E9RV0_a~Thpzg}sJFE>m=lW*s$U?)^2V`GcQ7AgGqY0hgm4ADoIIIJXB(B&Z z_A`P4NSw!PfYwy7RmG;HXu=^JUD$oFfz$j?c!Yr1ouFol3vB;DrU8!-mk(}2crV+= z_oSQ)I{p<8E>eR-h~6ARoYMFliW7l(UC;dyhmbEWRio7FR{E=Kr~+aU@PrIF*(_%b z13H=+C+)-2&mJVfo9Pi!BdY7|*;lI@51NR?*PZx`j&8^DGEwc`)ETYXs8q@1ty+YejxdYQgP*CDWf9RVPMqo{UmX_lYpG)07n@M z7^b#&>TTfDMFyF;MmEWO!}f^mb~Q1P)J8JCfQ%Nu@d)$7uKR2TiepM=`mxv@Q3VRISV>p(->cBh3{9T zdk@Y0gX(yaiBkJgSAJExADO?N9^LAsWrc$~Vp;rd=3eB+-S%)H!2Rw4^4Atx_>`*d ze?1OWglR)VP)S2WAaHVTW@&6?004NLeUUv#!$2IxUsJ`eN(VcLh-Rox z7DPoHwF*V35Nd^19ZW9$f+h_~i;JV+T5#}VvFhOBtgC~oAP9bdI5|2gx=4xtOA0Mw zJUH&hyL*qjcYshUGRs^Fr zd7t}p^r{()0X~sl*=mTEzVl8!W#GFFAQY#m1VBe97GI@Sb_u*3Mwd| zh%zFy>ZDjm(0dDA4|5+aDu9$1YH> z+V=OcZP!l#|1)rYT&=zlU~Q+6ajO(B~F-p}Zp z(m>xW(7opN*4W4C1CXSy;y1v-AuyVw>~)WKceeKS@0mt_KU!^aoi&k1f&c&jicm~c zMF5cikB^U#kdTp)k&=>uz*=jZ3?>FMq5?ez5Y_V)H~X-nw<0004WQchCi1)9jgeP@BeyhEhA~0nM=Qu_t zGQuO)h2hNzi4ew=?t38<;*@h=6JCQP*sux?-|0RZ!G@l6qa5MUm~L|sTOznpcjJEO vLqGpDOX{~YQqoW=VQiU?A;eTU_d9<9()}roVYHSX00000NkvXXu0mjf7=Yox literal 0 HcmV?d00001 diff --git a/plugin/zoom/resources/img/64/zoom_meet.png b/plugin/zoom/resources/img/64/zoom_meet.png new file mode 100644 index 0000000000000000000000000000000000000000..798ecbe65597695889129538ee56e87f6d3bd005 GIT binary patch literal 6382 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 literal 0 HcmV?d00001 diff --git a/plugin/zoom/resources/img/64/zoom_meet_na.png b/plugin/zoom/resources/img/64/zoom_meet_na.png new file mode 100644 index 0000000000000000000000000000000000000000..e313c79a14aced0dd60ecf435ac3769494882056 GIT binary patch literal 2917 zcmV-r3!3zaP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O=3$mg6`Q{KqNw2!u(4OM2jZOzT^+Xtv+tZxnU6xEJ)Q0Gk|fl1R=BD>irUjz zj+@B(^|$i5O};ks@9@}A1Sn0yMmeRGV?OpcdbDGJ@_CG-;~BBW1jH@G5@&4D;@ahi z10t5RlAug!fC+EYEUBiEou}eYv?RIanH6M>v;wFrIo)N6d*8bE8&R?Ij2N0C7K@nj z%P0J<^OGl-4TmTmddCWKCD&_+;mr9Pqd-Wsi#OT;-=2&9<3WF*s$h^d%!&iHqn}H3 zieI{wH_sX42<2%)G040Y07TfEAPh{P#Dim|$nlVrBu3zN4Yc-5-Xz8ivUOIK|(zVy#2GnPi zL4$`39d)#sW}a{plc!9bb+#45f~Qzz(c&dbS6$7ZYnH7T8Jn0|wRUIiV)fhe0c-JM zEp4WJoZMMMYY@3D5#61Ta|XsziC|ou0Vs5G&JK;GBxlY!J8FVaP=b);+=LmOF;G}m zOl0xS-4}B|;w_N=h`017=7LlAPcRoi-FM#Jur@u;jm_9;g^uZwVSU{S#S+Lv6DohV z{I?g%m4LG}kxI*~(GGf4*1@T=dWeNX>|N#pw20Cv#MzgDOHP2=Tl#dxsEs7gg>Ps= zD~4tR9;I=N+Psp~haa~xyf*x#tVB8bE?BeUH;cAVyW}x*^KJIn4@!<|`I0nfYXc_3 ze4eT-rrC8#F7uG0UFi00fN4(*d$`2e!;2@r4-8$gEcz{bx_(@AJ}$8fDtV0g75FC4Sj%i(jCj?-n< zW`!+f7@~!l^s1tE3JJ_ zct9z=n=E)5c-1f%x(Ux9L4z#jpzVLXWB0efvF>YxlGWg20YH;M$`PguOT zWkY!k4*ik6I{I9-yE6n4an)QXeB^l$5LCq$lmHnZ0gK z{Dba+egFnXABVGYr2L|uX?!!pt&o{FQqX{zcx(0tHZo{#xqy*f7`4Va;Y}@gq(RUT z*MkpNBLWhlo!Tp8OVt+KrND^)$lsD6*4ts5-tR?xV9MAfGM(O5M$k3@kI27jYmY; zIs^Frd7t}p^r{()0X~sl*=mTEzVl8!W#GFFAQY#m1VBe97GI@ zSb_u*3Mwd|h%zFy>ZDjm(0dDA4|5 z+aDu9$1YH>+V=OcZP!l#|1)rYT&=zlU~Q+6aj zO(B~F-p}Zp(m>xW(7opN*4W4C1CXSy;y1v-AuyVw>~)WKceeKS@0mt_KU!^aoi&k1 zf&c&jicm~cMF4RCkB^U#kdTp)k&=>uz*=jZ3?>FMq5?ez5Y_V)JUm2!0e0004W zQchC*;#PXGV`S4l)cR9M69m)%mrFc3yt+GKYD6;xDEL`$vs_x`VETNxT5o1|Is zigVK$%6AgFJI#_LCyvV?S4!o1Tvn3_Wc}hmhhVTHMPkUJSKr}}D{=##Jdzh_p2$&F z3S%HzzabT^&prnrnIZ3!ClvhvYg0l`0SPxiq$4ZHZDIHExUlkcJ9IW=SAw&O;c@$g znRN^37gXo%{Y7CFP^e&0bpq8>a8ci#SB3zsHE;^*ydfZK6SxFTULv@uWP-bAp~!eP|zAW!O$ugvw|xi#L^Hj z@6-(^nAY_}D7}JfCclBEgAkVB5$M^6CH`5AeuAjc@(6wzR7#@*vJ^Zgi}}_@3x9t! ziBDIlKsce0bjA@*X(XNVT$83WAENM%+rs#MTSvrsMZbSNdxIq + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/zoom/start.php b/plugin/zoom/start.php new file mode 100644 index 000000000..7b6bad7d6 --- /dev/null +++ b/plugin/zoom/start.php @@ -0,0 +1,118 @@ + 'Videoconference Zoom', +]; +Event::registerLog($logInfo); + +$course = api_get_course_entity(); +if (null === $course) { + api_not_allowed(true); +} + +$group = api_get_group_entity(); +$session = api_get_session_entity(); +$plugin = ZoomPlugin::create(); + +if (null !== $group) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(), + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.api_get_cidreq(), + 'name' => get_lang('GroupSpace').' '.$group->getName(), + ]; +} + +$url = api_get_self().'?'.api_get_cidreq(true, false).'&gidReq='; +$htmlHeadXtra[] = ''; + +$tool_name = $plugin->get_lang('ZoomVideoConferences'); +$tpl = new Template($tool_name); + +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; + +$isManager = $plugin->userIsCourseConferenceManager(); +if ($isManager) { + $groupId = api_get_group_id(); + $groups = GroupManager::get_groups(); + if (!empty($groups)) { + $form = new FormValidator('group_filter'); + $groupList[0] = get_lang('Select'); + foreach ($groups as $groupData) { + $itemGroupId = $groupData['iid']; + /*if (isset($meetingsGroup[$itemGroupId]) && $meetingsGroup[$itemGroupId] == 1) { + $groupData['name'] .= ' ('.get_lang('Active').')'; + }*/ + $groupList[$itemGroupId] = $groupData['name']; + } + $form->addSelect('group_id', get_lang('Groups'), $groupList, ['id' => 'group_select']); + $form->setDefaults(['group_id' => $groupId]); + $formToString = $form->returnForm(); + + $tpl->assign('group_form', $formToString); + } + + switch ($action) { + case 'delete': + $meeting = $plugin->getMeetingRepository()->findOneBy(['meetingId' => $_REQUEST['meetingId']]); + if ($meeting && $meeting->isCourseMeeting()) { + $plugin->deleteMeeting($meeting, api_get_self().'?'.api_get_cidreq()); + } + break; + } + + $user = api_get_user_entity(api_get_user_id()); + + $tpl->assign( + 'instant_meeting_form', + $plugin->getCreateInstantMeetingForm( + $user, + $course, + $group, + $session + )->returnForm() + ); + $tpl->assign( + 'schedule_meeting_form', + $plugin->getScheduleMeetingForm( + $user, + $course, + $group, + $session + )->returnForm() + ); +} + +try { + $tpl->assign( + 'meetings', + $plugin->getMeetingRepository()->courseMeetings($course, $group, $session) + ); +} catch (Exception $exception) { + Display::addFlash( + Display::return_message('Could not retrieve scheduled meeting list: '.$exception->getMessage(), 'error') + ); +} + +$tpl->assign('is_manager', $isManager); +$tpl->assign('content', $tpl->fetch('zoom/view/start.tpl')); +$tpl->display_one_col_template(); diff --git a/plugin/zoom/subscription.php b/plugin/zoom/subscription.php new file mode 100644 index 000000000..0470eca4f --- /dev/null +++ b/plugin/zoom/subscription.php @@ -0,0 +1,117 @@ +getMeetingRepository()->findOneBy(['meetingId' => $_REQUEST['meetingId']]); + +if (null === $meeting) { + api_not_allowed(true, $plugin->get_lang('MeetingNotFound')); +} + +if (false !== $meeting->isGlobalMeeting() + || false != $meeting->isCourseMeeting() + || 'true' !== $plugin->get('enableParticipantRegistration') + || !$meeting->requiresRegistration() +) { + api_not_allowed(true); +} + +$currentUser = api_get_user_entity(api_get_user_id()); +$userRegistrant = $meeting->getRegistrantByUser($currentUser); + +if ($meeting->isCourseMeeting()) { + api_protect_course_script(true); + + if (api_is_in_group()) { + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(), + 'name' => get_lang('Groups'), + ]; + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.api_get_cidreq(), + 'name' => get_lang('GroupSpace').' '.$meeting->getGroup()->getName(), + ]; + } +} + +$form = new FormValidator('subscription'); +$form->addHidden('meetingId', $meeting->getMeetingId()); + +if (!empty($userRegistrant)) { + $form->addButton( + 'unregister', + $plugin->get_lang('UnregisterMeToConference'), + 'user-times', + 'warning' + ); + + $form->addHtml( + '
    ' + .Display::url( + $plugin->get_lang('ViewMeeting'), + api_get_path(WEB_PLUGIN_PATH).'zoom/join_meeting.php?meetingId='.$meeting->getMeetingId(), + ['class' => 'btn btn-primary'] + ) + .'
    ' + ); +} else { + $filtered = array_filter( + $meeting->getRegistrableUsers(), + function (User $registableUser) use ($currentUser) { + return $registableUser->getId() === $currentUser->getId(); + } + ); + + if (empty($filtered)) { + api_not_allowed(true); + } + + $form->addButton( + 'register', + $plugin->get_lang('RegisterMeToConference'), + 'user-plus', + 'success' + ); +} + +if ($form->validate()) { + $values = $form->exportValues(); + + if (isset($values['unregister'])) { + $plugin->unregister($meeting, [$userRegistrant]); + } else { + $plugin->registerUsers($meeting, [$currentUser]); + } + + Display::addFlash( + Display::return_message($plugin->get_lang('RegisteredUserListWasUpdated'), 'success') + ); + + api_location('?meetingId='.$meeting->getMeetingId()); +} else { + $form->protect(); +} + +$view = new Template(''); +$view->assign('meeting', $meeting); +$view->assign('frm_register_unregister', $form->returnForm()); +$content = $view->fetch('zoom/view/subscription.tpl'); + +$view->assign('content', $content); +$view->display_one_col_template(); diff --git a/plugin/zoom/uninstall.php b/plugin/zoom/uninstall.php new file mode 100644 index 000000000..07d187f73 --- /dev/null +++ b/plugin/zoom/uninstall.php @@ -0,0 +1,8 @@ +uninstall(); diff --git a/plugin/zoom/view/activity.tpl b/plugin/zoom/view/activity.tpl new file mode 100644 index 000000000..494f2e25a --- /dev/null +++ b/plugin/zoom/view/activity.tpl @@ -0,0 +1,57 @@ +

    + {{ meeting.typeName }} {{ meeting.meetingId }} +

    + + + {{ 'Edit'|get_lang }} + + + + + + + +{# #} + + + + + + {% for activity in meeting.activities %} + + + + +{# #} + + + {% endfor %} + +
    {{ 'Type'|get_lang }}{{ 'Action'|get_plugin_lang('ZoomPlugin') }}{{ 'User'|get_lang }}{{ 'Date'|get_lang }}{{ 'Details'|get_lang }}
    + {{ activity.type }} + + {{ activity.name }} + + {{ activity.createdAt | api_convert_and_format_date(3)}} + #} +{# {% if _u.is_admin %}#} +{# #} +{# {{ activity.user.firstname }} {{ activity.user.lastname }} ({{ activity.user.username }})#} +{# #} +{# {% else %}#} +{# {{ activity.user.firstname }} {{ activity.user.lastname }} ({{ activity.user.username }})#} +{# {% endif %}#} +{# + {% if activity.eventDecoded.registrant %} + {{ 'User' | get_lang }} : + {{ activity.eventDecoded.registrant.first_name }} - + {{ activity.eventDecoded.registrant.last_name }} - + {{ activity.eventDecoded.registrant.email }} - + {{ activity.eventDecoded.registrant.status }} + {% endif %} + + {% if activity.eventDecoded.participant %} + {{ 'User' | get_lang }} : + {{ activity.eventDecoded.participant.user_name }} + {% endif %} +
    \ No newline at end of file diff --git a/plugin/zoom/view/calendar.tpl b/plugin/zoom/view/calendar.tpl new file mode 100644 index 000000000..122a936be --- /dev/null +++ b/plugin/zoom/view/calendar.tpl @@ -0,0 +1,324 @@ + + +
    + + + + diff --git a/plugin/zoom/view/join.tpl b/plugin/zoom/view/join.tpl new file mode 100644 index 000000000..368ae0720 --- /dev/null +++ b/plugin/zoom/view/join.tpl @@ -0,0 +1,196 @@ +{% include 'zoom/view/meeting_details.tpl' %} + +{% if is_conference_manager and meeting.isSignAttendance %} +

    + + {{ 'ConferenceWithAttendance'|get_plugin_lang('ZoomPlugin') }} +

    +{% endif %} + +
    + +{% set btn_start = '' %} + +{% if start_url %} + {% set btn_start %} + + {{ 'EnterMeeting'|get_plugin_lang('ZoomPlugin') }} + + {% endset %} +{% endif %} + +{% if not is_conference_manager %} + {% if meeting.isSignAttendance %} +
    +
    +
    +
    +

    + + {{ 'Attendance'|get_lang }} +

    +
    +
    +

    {{ meeting.reasonToSignAttendance }}

    + + {% if signature %} +
    + {{ 'SignatureDone'|get_plugin_lang('ZoomPlugin') }} +
    + {{ signature.registeredAt|api_convert_and_format_date(constant('DATE_TIME_FORMAT_LONG')) }} +
    +
    + {% else %} + {% set btn_start = '' %} + + {% if 'started' == meeting.meetingInfoGet.status %} + + + + + + {% endif %} + {% endif %} +
    +
    +
    +
    + {% endif %} +{% endif %} + +{{ btn_start }} + +{% if details_url %} + + {{ 'Details'|get_lang }} + +{% endif %} + +{% if btn_announcement %} + {{ btn_announcement }} +{% endif %} diff --git a/plugin/zoom/view/meeting.tpl b/plugin/zoom/view/meeting.tpl new file mode 100644 index 000000000..b43c52f0f --- /dev/null +++ b/plugin/zoom/view/meeting.tpl @@ -0,0 +1,136 @@ +

    + {{ meeting.typeName }} {{ meeting.meetingId }} + {% if meeting.meetingInfoGet.status %} + ({{ meeting.meetingInfoGet.status }}) + {% endif %} +

    + +
    + +{% if meeting.meetingInfoGet.status != 'finished' %} + + {{ 'ViewMeeting'|get_plugin_lang('ZoomPlugin') }} + +{% endif %} + +{% if isConferenceManager %} + {% if meeting.status == 'waiting' %} + + {{ 'StartMeeting'|get_plugin_lang('ZoomPlugin') }} + + {% endif %} + + + {{ 'Activity'|get_plugin_lang('ZoomPlugin') }} + + + + {{ 'Attendance'|get_lang }} + +{% endif %} +
    + +{% if isConferenceManager %} +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    +{% endif %} + +{% if currentUserJoinURL %} +{#

    #} +{# #} +{# {{ 'JoinMeeting'|get_plugin_lang('ZoomPlugin') }}#} +{# #} +{#

    #} +{% endif %} + +{% if isConferenceManager %} + {{ editMeetingForm }} + {{ deleteMeetingForm }} + {% if registerParticipantForm %} +
    + {{ registerParticipantForm }} + {% endif %} + + {% if registerPresenterForm %} + {{ registerPresenterForm }} + {% endif %} + + {{ fileForm }} + +
    +
    + {% if presenters %} +

    {{ 'Presenters'|get_plugin_lang('ZoomPlugin') }}

    + + {% for presenter in presenters %} + + + + {% endfor %} +
    + {{ presenter.fullName }} +
    + {% endif %} +
    +
    + {# {% if registrants and meeting.meetingInfoGet.settings.approval_type != 2 %}#} + {% if registrants.count > 0 %} + +

    {{ 'Users' | get_lang }}

    +
    + + {% for registrant in registrants %} + + + + + {% endfor %} +
    + {{ registrant.fullName }} + + {# {% if registrant.joinUrl %}#} + {# #} + {# {{ 'CopyJoinAsURL'|get_plugin_lang('ZoomPlugin') }}#} + {# #} + {# {% else %}#} + {# #} + {# {{ 'JoinURLNotAvailable'|get_plugin_lang('ZoomPlugin') }}#} + {# #} + {# {% endif %}#} +
    + {% endif %} +
    +
    +{% else %} + {% include 'zoom/view/meeting_details.tpl' %} +{% endif %} diff --git a/plugin/zoom/view/meeting_details.tpl b/plugin/zoom/view/meeting_details.tpl new file mode 100644 index 000000000..0b6c00efe --- /dev/null +++ b/plugin/zoom/view/meeting_details.tpl @@ -0,0 +1,23 @@ + + +
    + {% if meeting.requiresDateAndDuration %} +
    {{ 'StartTime'|get_lang }}
    +
    {{ meeting.formattedStartTime }}
    + +
    {{ 'Duration'|get_lang }}
    +
    {{ meeting.formattedDuration }}
    + {% endif %} + + {% if meeting.accountEmail %} +
    {{ 'AccountEmail'|get_lang }}
    +
    {{ meeting.accountEmail }}
    + {% endif %} +
    + +{% if meeting.agenda %} +

    {{ meeting.agenda|nl2br }}

    +{% endif %} diff --git a/plugin/zoom/view/meetings.tpl b/plugin/zoom/view/meetings.tpl new file mode 100644 index 000000000..0d7b39e1f --- /dev/null +++ b/plugin/zoom/view/meetings.tpl @@ -0,0 +1,64 @@ +{% import "default/document/recycle.tpl" as macro %} + +{{ schedule_form }} +{{ search_form }} + +{% if meetings %} +

    {{ 'MeetingsFound'|get_plugin_lang('ZoomPlugin') }}:

    + + + + + + + +{# #} +{# #} + {% if allow_recording %} + + {% endif %} + + + + + {% for meeting in meetings %} + + + + + +{# #} +{# #} + + + + {% endfor %} + +
    {{ 'Type'|get_lang }}{{ 'Topic'|get_plugin_lang('ZoomPlugin') }}{{ 'StartTime'|get_lang }}{{ 'ForEveryone'|get_plugin_lang('ZoomPlugin') }}{{ 'Course'|get_lang }}{{ 'Session'|get_lang }}{{ 'Recordings'|get_plugin_lang('ZoomPlugin') }}
    {{ meeting.typeName }}{{ meeting.topic }}{{ meeting.formattedStartTime }}{{ meeting.user ? 'No'|get_lang : 'Yes'|get_lang }}{{ meeting.course ? meeting.course : '-' }}{{ meeting.session ? meeting.session : '-' }} + {% if allow_recording and meeting.recordings.count > 0 %} + {% for recording in meeting.recordings %} +
    +
    + {{ recording.formattedStartTime }} ({{ recording.formattedDuration }}) {{ 'Password' | get_lang }}: {{ recording.recordingMeeting.password }} +
    +
    + +
    +
    + {% endfor %} + {% endif %} +
    + + {{ 'Details'|get_lang }} + +
    +{% endif %} \ No newline at end of file diff --git a/plugin/zoom/view/start.tpl b/plugin/zoom/view/start.tpl new file mode 100644 index 000000000..52bfb4835 --- /dev/null +++ b/plugin/zoom/view/start.tpl @@ -0,0 +1,60 @@ +{% if instant_meeting_form %} + {{ instant_meeting_form }} +{% endif %} + +{% if group_form %} + {{ group_form }} +{% endif %} + +{% if meetings.count %} + + + + + + + + + + + {% for meeting in meetings %} + + + + + + + + + {% endfor %} +
    {{ 'Type'|get_lang }}{{ 'Topic'|get_plugin_lang('ZoomPlugin') }}{{ 'Agenda'|get_plugin_lang('ZoomPlugin') }}{{ 'StartTime'|get_lang }}{{ 'Duration'|get_lang }}{{ 'Actions'|get_lang }}
    {{ meeting.typeName }} + {{ meeting.meetingInfoGet.topic }} + {{ meeting.webinarSchema.topic }} + + {{ meeting.meetingInfoGet.agenda|nl2br }} + {{ meeting.webinarSchema.agenda|nl2br }} + {{ meeting.formattedStartTime }}{{ meeting.formattedDuration }} + + {{ 'Join'|get_plugin_lang('ZoomPlugin') }} + + + {% if is_manager %} + + {{ 'Details'|get_plugin_lang('ZoomPlugin') }} + + + + {{ 'Delete'|get_lang }} + + {% endif %} +
    +{% endif %} + +{% if schedule_meeting_form %} + {{ schedule_meeting_form }} +{% endif %} \ No newline at end of file diff --git a/plugin/zoom/view/subscription.tpl b/plugin/zoom/view/subscription.tpl new file mode 100644 index 000000000..e8799fe9b --- /dev/null +++ b/plugin/zoom/view/subscription.tpl @@ -0,0 +1,3 @@ +{% include 'zoom/view/meeting_details.tpl' %} + +{{ frm_register_unregister }}